Copied plaintext files from Dieter. Added (broken) command alias support.
[uzbl-mobile] / uzbl.c
1 // Original code taken from the example webkit-gtk+ application. see notice below.
2 // Modified code is licensed under the GPL 3.  See LICENSE file.
3
4
5 /*
6  * Copyright (C) 2006, 2007 Apple Inc.
7  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include <gtk/gtk.h>
32 #include <gdk/gdk.h>
33 #include <gdk/gdkx.h>
34 #include <gdk/gdkkeys.h>
35 #include <gdk/gdkkeysyms.h>
36 #include <webkit/webkit.h>
37
38 #include <pthread.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <unistd.h>
44 #include <stdlib.h>
45
46 static GtkWidget*     main_window;
47 static GtkWidget*     modeline;
48 static WebKitWebView* web_view;
49
50 static gchar* history_file;
51 static gchar* home_page;
52 static gchar* uri       = NULL;
53 static gchar* fifodir   = NULL;
54 static char   fifopath[64];
55 static bool   modevis = FALSE;
56
57 static GOptionEntry entries[] =
58 {
59   { "uri",      'u', 0, G_OPTION_ARG_STRING, &uri,      "Uri to load",                                   NULL },
60   { "fifo-dir", 'd', 0, G_OPTION_ARG_STRING, &fifodir,  "Directory to place FIFOs",                      NULL },
61   { NULL }
62 };
63
64 struct command
65 {
66   char command[256];
67   void (*func)(WebKitWebView*);
68 };
69
70 static struct command commands[256];
71 static int            numcmds = 0;
72
73 struct alias
74 {
75   char alias[256];
76   char command[256];
77 };
78
79 static struct alias aliases[256];
80 static int          numalias = 0;
81
82 static void parse_command(char*);
83
84 static bool parse_modeline (GtkWidget* mode, GdkEventKey* event)
85 {
86   if ((event->type==GDK_KEY_PRESS) && (event->keyval==GDK_Return))
87     parse_command (gtk_entry_get_text (modeline));
88
89   return false;
90 }
91
92 static void log_history_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data)
93 {
94   strncpy (uri, webkit_web_frame_get_uri (frame), strlen (webkit_web_frame_get_uri (frame)));
95   
96   FILE * output_file = fopen (history_file, "a");
97   if (output_file == NULL)
98     {
99       fprintf (stderr, "Cannot open %s for logging\n", history_file);
100     }
101   else
102     {
103       time_t rawtime;
104       struct tm * timeinfo;
105       char buffer [80];
106       time (&rawtime);
107       timeinfo = localtime (&rawtime);
108       strftime (buffer,80,"%Y-%m-%d %H:%M:%S",timeinfo);
109       
110       fprintf (output_file, "%s %s\n",buffer, uri);
111       fclose (output_file);
112     }
113 }
114
115 static void toggle_command_mode ()
116 {
117   if (modevis)
118     {
119       gtk_widget_hide (modeline);
120       gtk_widget_grab_default (modeline);
121     }
122   else
123     {
124       gtk_widget_show (modeline);
125       gtk_widget_grab_focus (modeline);
126     }
127   modevis = ! modevis;
128 }
129
130 static gboolean key_press_cb (WebKitWebView* page, GdkEventKey* event)
131 {
132   gboolean result=FALSE; //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
133   if ((event->type==GDK_KEY_PRESS) && (event->keyval==GDK_Escape))
134     {
135       toggle_command_mode ();
136       result=TRUE;
137     }
138  
139   return(result);
140 }
141
142 static GtkWidget* create_browser ()
143 {
144   GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
145   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
146
147   web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
148   gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (web_view));
149
150   g_signal_connect (G_OBJECT (web_view), "load-committed", G_CALLBACK (log_history_cb), web_view);
151
152   return scrolled_window;
153 }
154
155 static GtkWidget* create_window ()
156 {
157   GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
158   gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
159   gtk_widget_set_name (window, "Uzbl Browser");
160   g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
161   g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(key_press_cb), NULL);
162
163   return window;
164 }
165
166 static GtkWidget* create_modeline ()
167 {
168   GtkWidget* modeline = gtk_entry_new ();
169   g_signal_connect (G_OBJECT (modeline), "key-press-event", G_CALLBACK(parse_modeline), modeline);
170
171   return modeline;
172 }
173
174 static void parse_command(char *command)
175 {
176   int  i    = 0;
177   bool done = false;
178   char *cmdstr = command;
179   void (*func)(WebKitWebView*);
180
181   printf("Checking aliases\n");
182   for (i = 0; i < numalias && ! done; i++)
183     {
184       if (!strncmp (cmdstr, aliases[i].alias, strlen (aliases[i].alias)))
185         {
186           strcpy(cmdstr, aliases[i].command);
187           done = true;
188         }
189     }
190
191   done = false;
192   printf("Checking commands\n");
193   for (i = 0; i < numcmds && ! done; i++)
194     {
195       if (!strncmp (cmdstr, commands[i].command, strlen (commands[i].command)))
196         {
197           func = commands[i].func;
198           done = true;
199         }
200     }
201
202   printf("Command identified as \"%s\"\n", cmdstr);
203
204   if (done)
205     {
206       func (web_view);
207     }
208   else
209     {
210       if (!strncmp ("http://", command, 7))
211         {
212           printf ("Loading URI \"%s\"\n", command);
213           uri = command;
214           webkit_web_view_load_uri (web_view, uri);
215         }
216     }
217 }
218
219 static void *control_fifo()
220 {
221   if (fifodir)
222     {
223       sprintf (fifopath, "%s/uzbl_%d", fifodir, getpid ());
224     }
225   else
226     {
227       sprintf (fifopath, "/tmp/uzbl_%d", getpid ());
228     }
229
230   if (mkfifo (fifopath, 0666) == -1)
231     {
232       printf ("Possible error creating fifo\n");
233     }
234
235     printf ("Opened control fifo in %s\n", fifopath);
236
237     while (true)
238       {
239         FILE *fifo = fopen(fifopath, "r");
240         if (!fifo)
241           {
242             printf("Could not open %s for reading\n", fifopath);
243             return NULL;
244           }
245         
246         char buffer[256];
247         memset (buffer, 0, sizeof (buffer));
248         while (!feof (fifo) && fgets (buffer, sizeof (buffer), fifo))
249           {
250             if (strcmp (buffer, "\n"))
251               {
252                 buffer[strlen (buffer) - 1] = '\0'; // Remove newline
253                 parse_command (buffer);
254               }
255           }
256       }
257     
258     return NULL;
259 }
260
261 static void add_command (char* cmdstr, void* function)
262 {
263   strncpy (commands[numcmds].command, cmdstr, strlen (cmdstr));
264   commands[numcmds].func = function;
265   numcmds++;
266 }
267
268 static void add_command_alias (char* alias, char* command)
269 {
270   strncpy (aliases[numalias].alias,   alias,   strlen (alias));
271   strncpy (aliases[numalias].command, command, strlen (command));
272   numalias++;
273 }
274
275 static bool setup_gtk (int argc, char* argv[])
276 {
277   gtk_init (&argc, &argv);
278
279   GtkWidget* vbox = gtk_vbox_new (FALSE, 0);
280   gtk_box_pack_start (GTK_BOX (vbox), create_browser (), TRUE, TRUE, 0);
281   modeline = create_modeline ();
282   gtk_box_pack_start (GTK_BOX (vbox), modeline, FALSE, FALSE, 0);
283
284   main_window = create_window ();
285   gtk_container_add (GTK_CONTAINER (main_window), vbox);
286   GError *error = NULL;
287
288   GOptionContext* context = g_option_context_new ("- The Usable Browser, controlled entirely through a FIFO");
289   g_option_context_add_main_entries (context, entries, NULL);
290   g_option_context_add_group (context, gtk_get_option_group (TRUE));
291   g_option_context_parse (context, &argc, &argv, &error);
292
293   if (uri)
294     {
295       webkit_web_view_load_uri (web_view, uri);
296     }
297
298   gtk_widget_grab_focus (GTK_WIDGET (web_view));
299   gtk_widget_show_all (main_window);
300   gtk_widget_hide(modeline);
301
302   return true;
303 }
304
305 static void setup_commands ()
306 {
307   //This func. is nice but currently it cannot be used for functions that require arguments or return data. --sentientswitch
308
309   add_command("back",     &webkit_web_view_go_back);
310   add_command("forward",  &webkit_web_view_go_forward);
311   add_command("refresh",  &webkit_web_view_reload); //Buggy
312   add_command("stop",     &webkit_web_view_stop_loading);
313   add_command("zoom in",  &webkit_web_view_zoom_in); //Can crash (when max zoom reached?).
314   add_command("zoom out", &webkit_web_view_zoom_out); //Crashes as zoom +
315   //add_command("get uri", &webkit_web_view_get_uri);
316 }
317
318 static void setup_threading ()
319 {
320   pthread_t control_thread;
321   pthread_create(&control_thread, NULL, control_fifo, NULL);
322 }
323
324 static void setup_settings ()
325 {
326   GKeyFile* config = g_key_file_new ();
327   gboolean  res    = g_key_file_load_from_file (config, "./config", G_KEY_FILE_NONE, NULL); //TODO: pass config file as argument
328
329   if (res)
330     {
331       printf ("Config loaded\n");
332     }
333   else
334     {
335       fprintf (stderr, "config loading failed\n"); //TODO: exit codes with gtk? 
336     }
337
338   history_file = g_key_file_get_value (config, "behavior", "history_file", NULL);
339   if (history_file)
340     {
341       printf ("Setting history file to: %s\n", history_file);
342     }
343   else
344     {
345       printf ("History logging disabled\n");
346     }
347
348   home_page = g_key_file_get_value (config, "behavior", "home_page", NULL);
349   if (home_page)
350     {
351       printf ("Setting home page to: %s\n", home_page);
352     }
353   else
354     {
355       printf ("Home page disabled\n");
356     }
357
358   /*GError *error = 0;
359   char   *keys  = g_key_file_get_keys (config, "alias", NULL, &error);
360
361   if (error) 
362     {
363       printf("Error: %n\n", error);
364     }
365   else
366     {
367       printf("Loading aliases\n");
368       while (keys != NULL &&  (*keys) != NULL)
369         {
370           char* value = g_key_file_get_value (config, (gchar *)"alias", (*keys), NULL);
371           add_command_alias((*keys), value);
372           ++keys;
373         }
374     }
375
376     Until segfaults is fixed, manually add aliases to test the rest of it. */
377   add_command_alias("b",  "back");
378   add_command_alias("f",  "forward");
379   add_command_alias("z+", "zoom in");
380   add_command_alias("z-", "zoom out");
381   add_command_alias("r",  "refresh");
382   add_command_alias("s",  "stop");
383 }
384
385 int main (int argc, char* argv[])
386 {
387   if (!g_thread_supported ())
388     g_thread_init (NULL);
389
390   setup_settings ();
391   setup_gtk (argc, argv);
392   setup_commands ();
393   setup_threading ();
394   gtk_main ();
395
396   printf ("Shutting down...\n");
397
398   unlink (fifopath);
399
400   return 0;
401 }