Merge branch 'master' of git://github.com/Dieterbe/uzbl into dieter/master
[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 static gboolean verbose = FALSE;
57 static Window   xwin    = NULL;
58
59 static GOptionEntry entries[] =
60 {
61   { "uri",      'u', 0, G_OPTION_ARG_STRING, &uri,      "Uri to load",                                   NULL },
62   { "fifo-dir", 'd', 0, G_OPTION_ARG_STRING, &fifodir,  "Directory to place FIFOs",                      NULL },
63   { NULL }
64 };
65
66 struct command
67 {
68   char command[256];
69   void (*func)(WebKitWebView*);
70 };
71
72 static struct command commands[256];
73 static int            numcmds = 0;
74
75 struct alias
76 {
77   char alias[256];
78   char command[256];
79 };
80
81 static struct alias aliases[256];
82 static int          numalias = 0;
83
84 static void parse_command(char*);
85
86 static bool parse_modeline (GtkWidget* mode, GdkEventKey* event)
87 {
88   if ((event->type==GDK_KEY_PRESS) && (event->keyval==GDK_Return))
89     parse_command (gtk_entry_get_text (modeline));
90  
91   return false;
92 }
93
94 static void log_history_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data)
95 {
96   strncpy (uri, webkit_web_frame_get_uri (frame), strlen (webkit_web_frame_get_uri (frame)));
97   
98   FILE * output_file = fopen(history_file, "a");
99   if (output_file == NULL)
100     {
101       fprintf(stderr, "Cannot open %s for logging\n", history_file);
102     }
103   else
104     {
105       time_t rawtime;
106       struct tm * timeinfo;
107       char buffer [80];
108       time ( &rawtime );
109       timeinfo = localtime ( &rawtime );
110       strftime (buffer,80,"%Y-%m-%d %H:%M:%S",timeinfo);
111       
112       fprintf(output_file, "%s %s\n",buffer, uri);
113       fclose(output_file);
114     }
115 }
116
117 static void toggle_command_mode ()
118 {
119   if (modevis)
120     {
121       gtk_widget_hide (modeline);
122       gtk_widget_grab_focus (web_view);
123     }
124   else
125     {
126       gtk_widget_show (modeline);
127       gtk_widget_grab_focus (modeline);
128     }
129   modevis = ! modevis;
130 }
131
132 static gboolean key_press_cb (WebKitWebView* page, GdkEventKey* event)
133 {
134   gboolean result=FALSE; //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
135   if ((event->type==GDK_KEY_PRESS) && (event->keyval==GDK_Escape))
136     {
137       toggle_command_mode ();
138       result=TRUE;
139     }
140  
141   return(result);
142 }
143
144 static GtkWidget* create_browser ()
145 {
146   GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
147   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
148
149   web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
150   gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (web_view));
151
152   g_signal_connect (G_OBJECT (web_view), "load-committed", G_CALLBACK (log_history_cb), web_view);
153
154   return scrolled_window;
155 }
156
157 static GtkWidget* create_window ()
158 {
159   GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
160   gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
161   gtk_widget_set_name (window, "Uzbl Browser");
162   g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
163   g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(key_press_cb), NULL);
164
165   return window;
166 }
167
168 static GtkWidget* create_modeline ()
169 {
170   GtkWidget* modeline = gtk_entry_new ();
171   g_signal_connect (G_OBJECT (modeline), "key-press-event", G_CALLBACK(parse_modeline), modeline);
172
173   return modeline;
174 }
175
176 static void parse_command(char *command)
177 {
178   int  i    = 0;
179   bool done = false;
180   char *cmdstr = command;
181   void (*func)(WebKitWebView*);
182
183   printf("Checking aliases\n");
184   for (i = 0; i < numalias && ! done; i++)
185     {
186       if (!strncmp (cmdstr, aliases[i].alias, strlen (aliases[i].alias)))
187         {
188           strcpy(cmdstr, aliases[i].command);
189           done = true;
190         }
191     }
192
193   done = false;
194   printf("Checking commands\n");
195   for (i = 0; i < numcmds && ! done; i++)
196     {
197       if (!strncmp (cmdstr, commands[i].command, strlen (commands[i].command)))
198         {
199           func = commands[i].func;
200           done = true;
201         }
202     }
203
204   printf("Command identified as \"%s\"\n", cmdstr);
205
206   if (done)
207     {
208       func (web_view);
209     }
210   else
211     {
212       if (!strncmp ("http://", command, 7))
213         {
214           printf ("Loading URI \"%s\"\n", command);
215           uri = command;
216           webkit_web_view_load_uri (web_view, uri);
217         }
218     }
219 }
220
221 static void *control_fifo()
222 {
223   if (fifodir)
224     {
225       sprintf (fifopath, "%s/uzbl_%d", fifodir, getpid ());
226     }
227   else
228     {
229       sprintf (fifopath, "/tmp/uzbl_%d", getpid ());
230     }
231
232   if (mkfifo (fifopath, 0666) == -1)
233     {
234       printf ("Possible error creating fifo\n");
235     }
236
237     printf ("Opened control fifo in %s\n", fifopath);
238
239     while (true)
240       {
241         FILE *fifo = fopen(fifopath, "r");
242         if (!fifo)
243           {
244             printf("Could not open %s for reading\n", fifopath);
245             return NULL;
246           }
247         
248         char buffer[256];
249         memset (buffer, 0, sizeof (buffer));
250         while (!feof (fifo) && fgets (buffer, sizeof (buffer), fifo))
251           {
252             if (strcmp (buffer, "\n"))
253               {
254                 buffer[strlen (buffer) - 1] = '\0'; // Remove newline
255                 parse_command (buffer);
256               }
257           }
258       }
259     
260     return NULL;
261 }
262
263 static void add_command (char* cmdstr, void* function)
264 {
265   strncpy (commands[numcmds].command, cmdstr, strlen (cmdstr));
266   commands[numcmds].func = function;
267   numcmds++;
268 }
269
270 static void add_command_alias (char* alias, char* command)
271 {
272   strncpy (aliases[numalias].alias,   alias,   strlen (alias));
273   strncpy (aliases[numalias].command, command, strlen (command));
274   numalias++;
275 }
276
277 static void setup_gtk (int argc, char* argv[])
278 {
279   gtk_init (&argc, &argv);
280
281   GtkWidget* vbox = gtk_vbox_new (FALSE, 0);
282   gtk_box_pack_start (GTK_BOX (vbox), create_browser (), TRUE, TRUE, 0);
283   modeline = create_modeline ();
284   gtk_box_pack_start (GTK_BOX (vbox), modeline, FALSE, FALSE, 0);
285
286   main_window = create_window ();
287   gtk_container_add (GTK_CONTAINER (main_window), vbox);
288   GError *error = NULL;
289
290   GOptionContext* context = g_option_context_new ("- The Usable Browser, controlled entirely through a FIFO");
291   g_option_context_add_main_entries (context, entries, NULL);
292   g_option_context_add_group (context, gtk_get_option_group (TRUE));
293   g_option_context_parse (context, &argc, &argv, &error);
294
295   if (uri)
296     {
297       webkit_web_view_load_uri (web_view, uri);
298     }
299
300   gtk_widget_grab_focus (GTK_WIDGET (web_view));
301   gtk_widget_show_all (main_window);
302   gtk_widget_hide(modeline);
303   gtk_widget_grab_focus (GTK_WIDGET (web_view));
304 }
305
306 static void setup_commands ()
307 {
308   //This func. is nice but currently it cannot be used for functions that require arguments or return data. --sentientswitch
309
310   add_command("back",     &webkit_web_view_go_back);
311   add_command("forward",  &webkit_web_view_go_forward);
312   add_command("refresh",  &webkit_web_view_reload); //Buggy
313   add_command("stop",     &webkit_web_view_stop_loading);
314   add_command("zoom in",  &webkit_web_view_zoom_in); //Can crash (when max zoom reached?).
315   add_command("zoom out", &webkit_web_view_zoom_out); //Crashes as zoom +
316   //add_command("get uri", &webkit_web_view_get_uri);
317 }
318
319 static void setup_threading ()
320 {
321   pthread_t control_thread;
322   pthread_create(&control_thread, NULL, control_fifo, NULL);
323 }
324
325 static void setup_settings ()
326 {
327   GKeyFile* config = g_key_file_new ();
328   gboolean  res    = g_key_file_load_from_file (config, "./sampleconfig", G_KEY_FILE_NONE, NULL); //TODO: pass config file as argument
329
330   if (res)
331     {
332       printf ("Config loaded\n");
333     }
334   else
335     {
336       fprintf (stderr, "config loading failed\n"); //TODO: exit codes with gtk? 
337     }
338
339   history_file = g_key_file_get_value (config, "behavior", "history_file", NULL);
340   if (history_file)
341     {
342       printf ("Setting history file to: %s\n", history_file);
343     }
344   else
345     {
346       printf ("History logging disabled\n");
347     }
348
349   home_page = g_key_file_get_value (config, "behavior", "home_page", NULL);
350   if (home_page)
351     {
352       printf ("Setting home page to: %s\n", home_page);
353     }
354   else
355     {
356       printf ("Home page disabled\n");
357     }
358
359   /*GError *error = 0;
360   char   *keys  = g_key_file_get_keys (config, "alias", NULL, &error);
361
362   if (error) 
363     {
364       printf("Error: %n\n", error);
365     }
366   else
367     {
368       printf("Loading aliases\n");
369       while (keys != NULL &&  (*keys) != NULL)
370         {
371           char* value = g_key_file_get_value (config, (gchar *)"alias", (*keys), NULL);
372           add_command_alias((*keys), value);
373           ++keys;
374         }
375     }
376
377     Until segfaults is fixed, manually add aliases to test the rest of it. */
378   add_command_alias("b",  "back");
379   add_command_alias("f",  "forward");
380   add_command_alias("z+", "zoom in");
381   add_command_alias("z-", "zoom out");
382   add_command_alias("r",  "refresh");
383   add_command_alias("s",  "stop");
384 }
385
386 int main (int argc, char* argv[])
387 {
388   if (!g_thread_supported ())
389     g_thread_init (NULL);
390
391   xwin = GDK_WINDOW_XID (GTK_WIDGET (main_window)->window);
392   printf("My X window id is %i\n",(int) xwin);
393
394   setup_settings ();
395   setup_gtk (argc, argv);
396   setup_commands ();
397   setup_threading ();
398   gtk_main ();
399
400   printf ("Shutting down...\n");
401
402   unlink (fifopath);
403
404   return 0;
405 }