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