1 /** EMULib Emulation Library *********************************/
2 /**                                                         **/
3 /**                        LibMaemo.c                       **/
4 /**                                                         **/
5 /** This file contains Maemo-dependent implementation       **/
6 /** parts of the emulation library.                         **/
7 /**                                                         **/
8 /** Copyright (C) Marat Fayzullin 1996-2009                 **/
9 /**     You are not allowed to distribute this software     **/
10 /**     commercially. Please, notify me, if you make any    **/
11 /**     changes to this file.                               **/
12 /*************************************************************/
13 #include "EMULib.h"
14 #include "LibARM.h"
15 #include "Console.h"
16 #include "Sound.h"
17 #include "Touch.h"
18 
19 #include <hildon/hildon-program.h>
20 #include <hildon/hildon-banner.h>
21 #include <hildon/hildon-note.h>
22 #include <hildon/hildon-file-chooser-dialog.h>
23 #include <hildon/hildon-file-system-model.h>
24 #include <gconf/gconf.h>
25 #include <gconf/gconf-client.h>
26 #include <gtk/gtkmain.h>
27 #include <gdk/gdk.h>
28 #include <libosso.h>
29 
30 #include <ctype.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <signal.h>
35 #include <fcntl.h>
36 #include <time.h>
37 #include <sys/time.h>
38 #include <sys/mman.h>
39 #include <sys/ioctl.h>
40 #include <linux/fb.h>
41 #include "omapfb.h"
42 
43 #define SCREEN_W     800
44 #define SCREEN_H     480
45 #define SCREEN_D     (sizeof(pixel)*8)
46 #define SCREEN_SIZE  (SCREEN_W*SCREEN_H*SCREEN_D/8)
47 #define FPS_COLOR    PIXEL(255,0,255)
48 #define CUE_COLOR    PIXEL(127,127,127)
49 
50 #define IS_HWSCALED ( \
51    FullScreen && FBImg.Data && (FBFD>=0) \
52 && ((Effects&(EFF_SOFTEN|EFF_TVLINES|EFF_SCALE))==EFF_SCALE))
53 
54 extern int MasterSwitch; /* Switches to turn channels on/off */
55 extern int MasterVolume; /* Master volume                    */
56 
57 static volatile int TimerReady = 0;   /* 1: Sync timer ready */
58 static volatile int FocusOUT   = 0;   /* 1: GTK focus out    */
59 static volatile unsigned int JoyState = 0; /* Joystick state */
60 static volatile unsigned int PenState = 0; /* Pen jstk state */
61 static volatile unsigned int PenDigit = 0; /* Pen dialpad s. */
62 static volatile unsigned int PenKey   = 0; /* Virt. kbd. key */
63 static volatile unsigned int LastKey  = 0; /* Last key prsd  */
64 static volatile unsigned int KeyModes = 0; /* SHIFT/CTRL/ALT */
65 static volatile unsigned int MseState = 0; /* Mouse state    */
66 
67 static int Effects    = EFF_SCALE|EFF_SAVECPU; /* EFF_* bits */
68 static int TimerON    = 0;  /* 1: sync timer is running      */
69 static int FullScreen = 0;  /* 1: full screen mode           */
70 static int RedrawAll  = 0;  /* 1: Redraw whole window/screen */
71 int  ARGC             = 0;  /* Copies of argc/argv           */
72 char **ARGV           = 0;
73 
74 static HildonProgram *HApp; /* Hildon application handle     */
75 static HildonWindow *HWnd;  /* Hildon window handle          */
76 static osso_context_t *Osso;/* Maemo services (DBus, etc.)   */
77 static GConfClient *Conf;   /* Configuration manager         */
78 static GtkMenu *Menu;       /* Top menu widget               */
79 
80 static Image GTKImg;        /* Display widget image buffer   */
81 static GtkWidget *Widget;   /* Main display widget           */
82 static GdkGC *Gc;           /* Drawing context for Widget    */
83 
84 static Image FBImg;         /* Frame buffer image buffer     */
85 static int FBFD;            /* Frame buffer file handle      */
86 
87 static Image OutImg;        /* Output image buffer (cropped) */
88 static int OutWidth;        /* Preferred output image width  */
89 static int OutHeight;       /* Preferred output image height */
90 
91 static int FrameCount;      /* Frame counter for EFF_SHOWFPS */
92 static int FrameRate;       /* Last frame rate value         */
93 static struct timeval TimeStamp; /* Last timestamp           */
94 
95 static int (*FileHandler)(const char *Filename);
96 static void (*MouseHandler)(unsigned int MouseState);
97 
98 static gint DBusHandler(const gchar *Interface,const gchar *Method,GArray *Args,gpointer Data,osso_rpc_t *Result);
99 static gint GTKKeyHandler(GtkWidget *SrcWidget,GdkEventKey *Event,gpointer Arg);
100 static gint GTKFocusHandler(GtkWidget *SrcWidget,GdkEventFocus *Event,gpointer Arg);
101 static gint GTKPenHandler(GtkWidget *SrcWidget,GdkEventCrossing *Event,gpointer Arg);
102 static void GTKMenuHandler(GtkAction *Action,int CmdID);
103 static void GTKQuitHandler(void);
104 static void TimerHandler(int Arg);
105 static void VKbdNewKey(unsigned int Key);
106 
107 /** TimerHandler() *******************************************/
108 /** The main timer handler used by SetSyncTimer().          **/
109 /*************************************************************/
TimerHandler(int Arg)110 void TimerHandler(int Arg)
111 {
112   /* Mark sync timer as "ready" */
113   ++TimerReady;
114   /* Repeat signal next time */
115   signal(Arg,TimerHandler);
116 }
117 
118 /** VKbdNewKey() *********************************************/
119 /** "Press" or "release" a virtual keyboard key.            **/
120 /*************************************************************/
VKbdNewKey(unsigned int Key)121 void VKbdNewKey(unsigned int Key)
122 {
123   GdkEventKey KE;
124   unsigned int J;
125 
126   /* Have to have virtual keyboard enabled */
127   if(!(Effects&EFF_VKBD)) Key=0;
128 
129   /* Remap CON_* keys to GDK_* keys */
130   switch(Key)
131   {
132     case CON_F1: case CON_F2: case CON_F3: case CON_F4:
133     case CON_F5: case CON_F6: case CON_F7: case CON_F8:
134       J = Key-CON_F1+GDK_F1;
135       break;
136     case '^':        J = GDK_Caps_Lock;break;
137     case CON_INSERT: J = GDK_Insert;break;
138     case CON_DELETE: J = GDK_Delete;break;
139     case CON_BS:     J = GDK_BackSpace;break;
140     case CON_TAB:    J = GDK_Tab;break;
141     case CON_ENTER:  J = GDK_KP_Enter;break;
142     case CON_ESCAPE: J = GDK_Escape;break;
143     default:         J = Key;break;
144   }
145 
146   /* If virtual key changed... */
147   if(Key!=(PenKey&0xFF))
148   {
149     /* If key has been pressed before... */
150     if(PenKey)
151     {
152       KE.type   = GDK_KEY_RELEASE;
153       KE.keyval = PenKey>>8;
154       GTKKeyHandler(Widget,&KE,0);
155     }
156     /* New key */
157     PenKey=Key|(J<<8);
158     if(PenKey)
159     {
160       KE.type   = GDK_KEY_PRESS;
161       KE.keyval = J;
162       GTKKeyHandler(Widget,&KE,0);
163     }
164   }
165 }
166 
167 /** InitMaemo() **********************************************/
168 /** Initialize Unix/GTK resources and set initial window    **/
169 /** title and dimensions.                                   **/
170 /*************************************************************/
InitMaemo(const char * Title,int Width,int Height,const char * Service)171 int InitMaemo(const char *Title,int Width,int Height,const char *Service)
172 {
173   static gint8 DashList[] = { 1,4 };
174   GtkWidget *Item;
175   GdkColor Color;
176   char *Object;
177   int J;
178 
179   /* Initialize variables */
180   OutImg.Data = 0;
181   OutWidth    = Width;
182   OutHeight   = Height;
183   FBImg.Data  = 0;
184   FBFD        = -1;
185   GTKImg.Data = 0;
186   Widget      = 0;
187   Gc          = 0;
188   FocusOUT    = 0;
189   TimerON     = 0;
190   TimerReady  = 0;
191   FullScreen  = 0;
192   RedrawAll   = 0;
193   JoyState    = 0;
194   PenState    = 0;
195   MseState    = 0;
196   PenDigit    = 0;
197   PenKey      = 0;
198   LastKey     = 0;
199   KeyModes    = 0;
200   Osso        = 0;
201   Conf        = 0;
202   FileHandler = 0;
203   FrameCount  = 0;
204   FrameRate   = 0;
205 
206   /* Get initial timestamp */
207   gettimeofday(&TimeStamp,0);
208 
209   /* Initialize GTK and GLib type system */
210   gtk_init(&ARGC,&ARGV);
211   g_type_init();
212 
213   /* Initialize DBus access */
214   Osso = osso_initialize(Service,"1.0.0",TRUE,0);
215   if(!Osso) return(0);
216 
217   /* If DBus service name given... */
218   if(Service)
219   {
220     /* Create Osso object name */
221     Object = strdup(Service);
222     if(!Object) { osso_deinitialize(Osso);Osso=0;return(0); }
223     for(J=0;Object[J];++J) if(Object[J]=='.') Object[J]='/';
224     /* Attach DBus message handler */
225     if(osso_rpc_set_cb_f(Osso,Service,Object,Service,DBusHandler,0)!=OSSO_OK)
226     { free(Object);osso_deinitialize(Osso);Osso=0;return(0); }
227     /* Done with Osso object name */
228     free(Object);
229   }
230 
231   /* Create Hildon application, window, and top menu */
232   HApp = HILDON_PROGRAM(hildon_program_get_instance());
233   g_set_application_name(Title);
234   HWnd = HILDON_WINDOW(hildon_window_new());
235   hildon_program_add_window(HApp,HWnd);
236   Menu = GTK_MENU(gtk_menu_new());
237   hildon_window_set_menu(HWnd,Menu);
238 
239   /* Make window background black */
240   Color.red=Color.green=Color.blue=0;
241   gtk_widget_modify_bg(GTK_WIDGET(HWnd),GTK_STATE_NORMAL,&Color);
242 
243   /* Create new widget image buffer */
244   if(!NewImage(&GTKImg,SCREEN_W,SCREEN_H)) return(0);
245 
246   /* Create widget from the image */
247   Widget=gtk_image_new_from_image(GTKImg.GImg,0);
248   /* Widget must exist now */
249   if(!Widget) { FreeImage(&GTKImg);return(0); }
250 
251   /* Add widget to Hildon window */
252   gtk_container_add(GTK_CONTAINER(HWnd),Widget);
253 
254   /* Add QUIT menu */
255   if(Menu&&(Item=gtk_menu_item_new_with_label("Quit")))
256   {
257     gtk_menu_append(Menu,Item);
258     g_signal_connect(G_OBJECT(Item),"activate",G_CALLBACK(GTKQuitHandler),0);
259     gtk_widget_show_all(GTK_WIDGET(Menu));
260   }
261 
262   /* Connect signals */
263   g_signal_connect(G_OBJECT(HWnd),"delete_event",G_CALLBACK(GTKQuitHandler),0);
264   g_signal_connect(G_OBJECT(HWnd),"key_press_event",G_CALLBACK(GTKKeyHandler),0);
265   g_signal_connect(G_OBJECT(HWnd),"key_release_event",G_CALLBACK(GTKKeyHandler),0);
266   g_signal_connect(G_OBJECT(HWnd),"focus_in_event",G_CALLBACK(GTKFocusHandler),0);
267   g_signal_connect(G_OBJECT(HWnd),"focus_out_event",G_CALLBACK(GTKFocusHandler),0);
268   g_signal_connect(G_OBJECT(HWnd),"enter_notify_event",G_CALLBACK(GTKPenHandler),0);
269   g_signal_connect(G_OBJECT(HWnd),"leave_notify_event",G_CALLBACK(GTKPenHandler),0);
270   g_signal_connect(G_OBJECT(HWnd),"motion_notify_event",G_CALLBACK(GTKPenHandler),0);
271 
272   /* Motion events do not work without this */
273   gtk_widget_set_events(GTK_WIDGET(HWnd),gtk_widget_get_events(GTK_WIDGET(HWnd))|GDK_BUTTON_MOTION_MASK|GDK_POINTER_MOTION_HINT_MASK);
274   gtk_widget_set_extension_events(GTK_WIDGET(HWnd),GDK_EXTENSION_EVENTS_CURSOR);
275 
276   /* Show menu and window */
277   gtk_widget_show_all(GTK_WIDGET(Menu));
278   gtk_widget_show_all(GTK_WIDGET(HWnd));
279 
280   /* Get configuration client handle */
281   Conf=gconf_client_get_default();
282 
283   /* Create drawing context */
284   Gc=gdk_gc_new(GDK_DRAWABLE(gtk_widget_get_parent_window(Widget)));
285 
286   /* We are going to use gray dotted lines */
287   Color.red=Color.blue=Color.green=0x7FFF;
288   gdk_gc_set_rgb_fg_color(Gc,&Color);
289   gdk_gc_set_line_attributes(Gc,0,GDK_LINE_ON_OFF_DASH,GDK_CAP_BUTT,GDK_JOIN_MITER);
290   gdk_gc_set_dashes(Gc,0,DashList,sizeof(DashList));
291   gdk_gc_set_subwindow(Gc,GDK_INCLUDE_INFERIORS);
292 
293   /* Open frame buffer device */
294   FBFD = open("/dev/fb0",O_RDWR);
295   if(FBFD>=0)
296   {
297     /* Map hardware frame buffer */
298     FBImg.Data = (pixel *)mmap(0,SCREEN_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,FBFD,0);
299     if(!FBImg.Data) { close(FBFD);FBFD=-1; }
300     else
301     {
302       /* Initialize frame buffer image */
303       FBImg.Cropped = 1;
304       FBImg.W = SCREEN_W;
305       FBImg.H = SCREEN_H;
306       FBImg.L = SCREEN_W;
307       FBImg.D = SCREEN_D;
308     }
309   }
310 
311   /* Force full-screen mode if requested */
312   if(Widget&&(Effects&EFF_FULLSCR))
313   {
314     FullScreen=1;
315     gdk_window_fullscreen(gtk_widget_get_parent_window(Widget));
316   }
317 
318   /* Create output image buffer */
319   GTKResizeVideo(Width,Height);
320 
321   /* Done */
322   return(1);
323 }
324 
325 /** TrashMaemo() *********************************************/
326 /** Free resources allocated in InitMaemo()                 **/
327 /*************************************************************/
TrashMaemo(void)328 void TrashMaemo(void)
329 {
330   /* Free all allocated resources */
331   if(Gc)   { gdk_gc_destroy(Gc);Gc=0; }
332   if(Conf) { g_object_unref(Conf);Conf=0; }
333   if(Osso) { osso_deinitialize(Osso);Osso=0; }
334 
335   /* Unmap hardware frame buffer */
336   if(FBImg.Data) munmap(FBImg.Data,FBImg.L*FBImg.H*(FBImg.D>>3));
337   /* Close frame buffer device */
338   if(FBFD>=0) { close(FBFD);FBFD=-1; }
339 
340   /* Free image buffers */
341   FreeImage(&OutImg);
342   FreeImage(&GTKImg);
343   FreeImage(&FBImg);
344 }
345 
346 /** ShowVideo() **********************************************/
347 /** Show "active" image at the actual screen or window.     **/
348 /*************************************************************/
ShowVideo(void)349 int ShowVideo(void)
350 {
351   Image *Output,CueImg;
352   int DW,DH,SW,SH;
353 
354   /* Have to have a window and a valid video buffer */
355   if(!Widget||!Gc||!VideoImg||!VideoImg->GImg) return(0);
356 
357   /* When in full screen mode, use hardware frame buffer */
358   Output = FullScreen&&FBImg.Data&&(FBFD>=0)? &FBImg:&OutImg;
359 
360   /* May have to wait for FB->LCD data transfer to complete */
361   if(Output==&FBImg) ioctl(FBFD,OMAPFB_SYNC_GFX);
362 
363   /* When redrawing whole screen, clear buffers */
364   if(RedrawAll) ClearImage(Output==&FBImg? &FBImg:&GTKImg,PIXEL(0,0,0));
365 
366   /* Process image as requested */
367   if(Effects&EFF_SOFTEN)
368   {
369     DW = VideoW/VideoH;
370     DH = VideoH/VideoW;
371     SW = VideoW*(DH>0? DH:1);
372     SH = VideoH*(DW>0? DW:1);
373     DW = SW*Output->H/SH;
374     DH = SH*Output->W/SW;
375     if(DW>Output->W) DW=Output->W; else DH=Output->H;
376     CropImage(&CueImg,Output,(Output->W-DW)>>1,(Output->H-DH)>>1,DW,DH);
377     SoftenImage(&CueImg,VideoImg,VideoX,VideoY,VideoW,VideoH);
378   }
379   else if((Effects&EFF_TVLINES)||((Effects&EFF_SCALE)&&(Output!=&FBImg)))
380   {
381     DW = ARMScaleImage(Output,VideoImg,VideoX,VideoY,VideoW,VideoH,0);
382     DH = DW>>16;
383     DW&= 0xFFFF;
384   }
385   else
386   {
387     IMGCopy(
388       Output,(Output->W-VideoW)>>1,(Output->H-VideoH)>>1,
389       VideoImg,VideoX,VideoY,VideoW,VideoH,-1
390     );
391     DW = VideoW;
392     DH = VideoH;
393   }
394 
395   /* Apply TV scanlines if requested */
396   if(Effects&EFF_TVLINES)
397   {
398     /* Make sure DW is a multiple of 16 pixels for optimization */
399     DW = (DW+15)&~15;
400     TelevizeImage(Output,(Output->W-DW)>>1,(Output->H-DH)>>1,DW,DH);
401   }
402 
403   /* Show framerate if requested */
404   if((Effects&EFF_SHOWFPS)&&(FrameRate>0))
405   {
406     char S[8];
407     sprintf(S,"%dfps",FrameRate);
408     PrintXY(
409       Output,S,
410       ((Output->W-DW)>>1)+8,((Output->H-DH)>>1)+8,
411       FPS_COLOR,-1
412     );
413   }
414 
415   /* Wait for sync timer if requested */
416   if(Effects&EFF_SYNC) WaitSyncTimer();
417 
418   /* When drawing directly to hardware frame buffer... */
419   if(Output==&FBImg)
420   {
421     static struct omapfb_update_window FBUpdate;
422 
423     /* For simple scaling, use hardware scaler */
424     if((Effects&(EFF_SOFTEN|EFF_TVLINES|EFF_SCALE))==EFF_SCALE)
425     {
426       SW = FBImg.W/DW;
427       SH = FBImg.H/DH;
428       SH = SW<SH? SW:SH;
429       SW = DW*SH;
430       SH = DH*(DW/DH)*SW/DW;
431       SH = SH<=FBImg.H? SH:FBImg.H;
432       /* Overlay cue lines and virtual keyboard, if requested */
433       if(Effects&(EFF_PENCUES|EFF_VKBD))
434       {
435         /* Compute effecive rectangle for the cues */
436         DW = FBImg.W*DW/SW;
437         DH = FBImg.H*DH/SH;
438         CropImage(&CueImg,&FBImg,(FBImg.W-DW)>>1,(FBImg.H-DH)>>1,DW,DH);
439         /* Draw cue lines and virtual keyboard */
440         if(Effects&EFF_PENCUES) DrawPenCues(&CueImg,Effects&EFF_DIALCUES,CUE_COLOR);
441         if(Effects&EFF_VKBD)    DrawKeyboard(&CueImg,KeyModes|(PenKey&0xFF));
442         /* Will be scaling to the whole buffer */
443         SW = FBImg.W;
444         SH = FBImg.H;
445       }
446     }
447     else
448     {
449       /* Overlay cue lines and virtual keyboard, if requested */
450       if(Effects&(EFF_PENCUES|EFF_VKBD))
451       {
452         /* Draw cue lines and virtual keyboard */
453         if(Effects&EFF_PENCUES) DrawPenCues(&FBImg,Effects&EFF_DIALCUES,CUE_COLOR);
454         if(Effects&EFF_VKBD)    DrawKeyboard(&FBImg,KeyModes|(PenKey&0xFF));
455         /* Will be transferring the whole buffer */
456         DW = FBImg.W;
457         DH = FBImg.H;
458       }
459       /* If redrawing whole screen... */
460       if(RedrawAll) { DW=FBImg.W;DH=FBImg.H; }
461       /* No scaling */
462       SW = DW;
463       SH = DH;
464     }
465 
466 //printf("%dx%d ==> %dx%d ==> %dx%d\n",VideoW,VideoH,DW,DH,SW,SH);
467 
468     /* Set up frame buffer update */
469     FBUpdate.width      = DW;
470     FBUpdate.height     = DH;
471     FBUpdate.x          = (FBImg.W-DW)>>1;
472     FBUpdate.y          = (FBImg.H-DH)>>1;
473     FBUpdate.out_width  = SW;
474     FBUpdate.out_height = SH;
475     FBUpdate.out_x      = (FBImg.W-SW)>>1;
476     FBUpdate.out_y      = (FBImg.H-SH)>>1;
477     FBUpdate.format     = OMAPFB_COLOR_RGB565;
478 
479     /* If VSync requested, modify transfer flags */
480     if(Effects&EFF_VSYNC)
481       FBUpdate.format|= OMAPFB_FORMAT_FLAG_TEARSYNC;
482 
483     /* Request FB transfer to LCD video memory */
484     ioctl(FBFD,OMAPFB_UPDATE_WINDOW,&FBUpdate);
485   }
486   else
487   {
488     /* Drawing to a widget! */
489     SW = Widget->allocation.width;
490     SH = Widget->allocation.height;
491 
492     /* Overlay cue lines and virtual keyboard, if requested */
493     if(Effects&(EFF_PENCUES|EFF_VKBD))
494     {
495       /* Compute effecive rectangle for the cues */
496       CropImage(&CueImg,&GTKImg,(GTKImg.W-SW)>>1,(GTKImg.H-SH)>>1,SW,SH);
497       /* Draw cue lines and virtual keyboard */
498       if(Effects&EFF_PENCUES) DrawPenCues(&CueImg,Effects&EFF_DIALCUES,CUE_COLOR);
499       if(Effects&EFF_VKBD)    DrawKeyboard(&CueImg,KeyModes|(PenKey&0xFF));
500       /* Updating full rectangle */
501       DW = SW;
502       DH = SH;
503     }
504 
505     /* If redrawing whole screen... */
506     if(RedrawAll) { DW=SW;DH=SH; }
507 
508     /* Drawing to the widget (why need to add 32 to width?) */
509     gtk_widget_queue_draw_area(Widget,(SW-DW-32)>>1,(SH-DH)>>1,DW+32,DH);
510 
511     /* Make sure drawing completes */
512     gdk_window_process_updates(gtk_widget_get_parent_window(Widget),TRUE);
513   }
514 
515   /* Done */
516   if(RedrawAll) --RedrawAll;
517   return(1);
518 }
519 
520 /** NewImage() ***********************************************/
521 /** Create a new image of the given size. Returns pointer   **/
522 /** to the image data on success, 0 on failure.             **/
523 /*************************************************************/
NewImage(Image * Img,int Width,int Height)524 pixel *NewImage(Image *Img,int Width,int Height)
525 {
526   GdkVisual *GV;
527 
528 #if defined(BPP8) || defined(BPP16) || defined(BPP24) || defined(BPP32)
529   GV = gdk_visual_get_best_with_depth(SCREEN_D);
530 #else
531   GV = gdk_visual_get_best();
532 #endif
533 
534   /* Create new GdkImage object */
535   Img->GImg    = GV? gdk_image_new(GDK_IMAGE_FASTEST,GV,Width,Height):0;
536   Img->Cropped = 0;
537 
538   /* If GdkImage created... */
539   if(Img->GImg)
540   {
541     Img->Data = (pixel *)Img->GImg->mem;
542     Img->W    = Img->GImg->width;
543     Img->H    = Img->GImg->height;
544     Img->L    = Img->GImg->bpl/Img->GImg->bpp;
545     Img->D    = Img->GImg->bits_per_pixel;
546   }
547   else
548   {
549     Img->Data = 0;
550     Img->W    = 0;
551     Img->H    = 0;
552     Img->L    = 0;
553     Img->D    = 0;
554   }
555 
556   /* Done */
557   return(Img->Data);
558 }
559 
560 /** FreeImage() **********************************************/
561 /** Free previously allocated image.                        **/
562 /*************************************************************/
FreeImage(Image * Img)563 void FreeImage(Image *Img)
564 {
565   /* Free GdkImage object */
566   if(Img->GImg) gdk_image_destroy(Img->GImg);
567 
568   /* Image gone */
569   Img->GImg = 0;
570   Img->Data = 0;
571   Img->W    = 0;
572   Img->H    = 0;
573   Img->L    = 0;
574   Img->D    = 0;
575 }
576 
577 /** CropImage() **********************************************/
578 /** Create a subimage Dst of the image Src. Returns Dst.    **/
579 /*************************************************************/
CropImage(Image * Dst,const Image * Src,int X,int Y,int W,int H)580 Image *CropImage(Image *Dst,const Image *Src,int X,int Y,int W,int H)
581 {
582   GenericCropImage(Dst,Src,X,Y,W,H);
583   Dst->GImg = 0;
584   return(Dst);
585 }
586 
587 /** SetVideo() ***********************************************/
588 /** Set part of the image as "active" for display.          **/
589 /*************************************************************/
SetVideo(Image * Img,int X,int Y,int W,int H)590 void SetVideo(Image *Img,int X,int Y,int W,int H)
591 {
592   /* Call default SetVideo() function */
593   GenericSetVideo(Img,X,Y,W,H);
594   /* Automatically resize video output */
595   GTKResizeVideo(OutWidth,OutHeight);
596 }
597 
598 /** GetJoystick() ********************************************/
599 /** Get the state of joypad buttons (1="pressed"). Refer to **/
600 /** the BTN_* #defines for the button mappings.             **/
601 /*************************************************************/
GetJoystick(void)602 unsigned int GetJoystick(void)
603 {
604   /* Count framerate */
605   if((Effects&EFF_SHOWFPS)&&(++FrameCount>=300))
606   {
607     struct timeval NewTS;
608     int Time;
609 
610     gettimeofday(&NewTS,0);
611     Time       = (NewTS.tv_sec-TimeStamp.tv_sec)*1000
612                + (NewTS.tv_usec-TimeStamp.tv_usec)/1000;
613     FrameRate  = 1000*FrameCount/(Time>0? Time:1);
614     TimeStamp  = NewTS;
615     FrameCount = 0;
616     FrameRate  = FrameRate>999? 999:FrameRate;
617   }
618 
619   /* Process any accumulated keypresses, etc. */
620   GTKProcessEvents();
621 
622   /* Return joystick state, with modal keys */
623   return(JoyState|KeyModes);
624 }
625 
626 /** GetMouse() ***********************************************/
627 /** Get mouse position and button states in the following   **/
628 /** format: RMB.LMB.Y[29-16].X[15-0].                       **/
629 /*************************************************************/
GetMouse(void)630 unsigned int GetMouse(void)
631 {
632   gint X,Y;
633 
634   /* Process events */
635   GTKProcessEvents();
636   /* Have to have a widget */
637   if(!Widget) return(0);
638   /* Get pointer coordinates */
639   gtk_widget_get_pointer(Widget,&X,&Y);
640   /* Translate, truncate, and scale coordinates */
641   X = VideoW*(X-((Widget->allocation.width-OutImg.W)>>1))/OutImg.W;
642   Y = VideoH*(Y-((Widget->allocation.height-OutImg.H)>>1))/OutImg.H;
643   /* Do not return clicks outside of the widget */
644   if((X<0)||(Y<0)||(X>=VideoW)||(Y>=VideoH)) MseState=0;
645   X = X<0? 0:X>=VideoW? VideoW-1:X;
646   Y = Y<0? 0:Y>=VideoH? VideoH-1:Y;
647   /* Combine with mouse keys */
648   return((X&0xFFFF)|((Y&0x3FFF)<<16)|MseState);
649 }
650 
651 /** GetKey() *************************************************/
652 /** Get currently pressed key or 0 if none pressed. Returns **/
653 /** CON_* definitions for arrows and special keys.          **/
654 /*************************************************************/
GetKey(void)655 unsigned int GetKey(void)
656 {
657   unsigned int J;
658 
659   GTKProcessEvents();
660   J=LastKey;
661   LastKey=0;
662   return(J);
663 }
664 
665 /** WaitKey() ************************************************/
666 /** Wait for a key to be pressed. Returns CON_* definitions **/
667 /** for arrows and special keys.                            **/
668 /*************************************************************/
WaitKey(void)669 unsigned int WaitKey(void)
670 {
671   unsigned int J;
672 
673   /* Swallow current keypress */
674   GetKey();
675   /* Wait in 100ms increments for a new keypress */
676   while(!(J=GetKey())&&VideoImg) usleep(100000);
677   /* Return key code */
678   return(J);
679 }
680 
681 /** WaitKeyOrMouse() *****************************************/
682 /** Wait for a key or a mouse button to be pressed. Returns **/
683 /** the same result as GetMouse(). If no mouse buttons      **/
684 /** reported to be pressed, do GetKey() to fetch a key.     **/
685 /*************************************************************/
WaitKeyOrMouse(void)686 unsigned int WaitKeyOrMouse(void)
687 {
688   unsigned int J;
689 
690   /* Swallow current keypress */
691   GetKey();
692   /* Make sure mouse keys are not pressed */
693   while(GetMouse()&0xC0000000) usleep(100000);
694   /* Wait in 100ms increments for a key or mouse click */
695   while(!(J=GetKey())&&!(GetMouse()&0xC0000000)&&VideoImg) usleep(100000);
696   /* Place key back into the buffer and return mouse state */
697   LastKey=J;
698   return(GetMouse());
699 }
700 
701 /** WaitSyncTimer() ******************************************/
702 /** Wait for the timer to become ready.                     **/
703 /*************************************************************/
WaitSyncTimer(void)704 void WaitSyncTimer(void)
705 {
706   /* Wait in 1ms increments until timer becomes ready */
707   while(!TimerReady&&TimerON&&VideoImg) usleep(1000);
708   /* Warn of missed timer events */
709   if((TimerReady>1)&&(Effects&EFF_VERBOSE))
710     printf("WaitSyncTimer(): Missed %d timer events.\n",TimerReady-1);
711   /* Reset timer */
712   TimerReady=0;
713 }
714 
715 /** SyncTimerReady() *****************************************/
716 /** Return 1 if sync timer ready, 0 otherwise.              **/
717 /*************************************************************/
SyncTimerReady(void)718 int SyncTimerReady(void)
719 {
720   /* Return whether timer is ready or not */
721   return(TimerReady||!TimerON||!VideoImg);
722 }
723 
724 /** SetSyncTimer() *******************************************/
725 /** Set synchronization timer to a given frequency in Hz.   **/
726 /*************************************************************/
SetSyncTimer(int Hz)727 int SetSyncTimer(int Hz)
728 {
729   struct itimerval TimerValue;
730 
731   /* Compute and set timer period */
732   TimerValue.it_interval.tv_sec  =
733   TimerValue.it_value.tv_sec     = 0;
734   TimerValue.it_interval.tv_usec =
735   TimerValue.it_value.tv_usec    = Hz? 1000000L/Hz:0;
736 
737   /* Set timer */
738   if(setitimer(ITIMER_REAL,&TimerValue,NULL)) return(0);
739 
740   /* Set timer signal */
741   signal(SIGALRM,TimerHandler);
742 
743   /* Done */
744   TimerON=Hz;
745   return(1);
746 }
747 
748 /** ChangeDir() **********************************************/
749 /** This function is a wrapper for chdir().                 **/
750 /*************************************************************/
ChangeDir(const char * Name)751 int ChangeDir(const char *Name) { return(chdir(Name)); }
752 
753 /** GTKProcessEvents() ***************************************/
754 /** Process GTK event messages.                             **/
755 /*************************************************************/
GTKProcessEvents(void)756 void GTKProcessEvents(void)
757 {
758   do
759   {
760     /* Process all accumulated GTK events */
761     while(gtk_events_pending()) gtk_main_iteration_do(FALSE);
762     /* Wait in 1s increments for the focus to return */
763     if(FocusOUT) sleep(1);
764   }
765   while(FocusOUT);
766 }
767 
768 /** GTKSetEffects() ******************************************/
769 /** Set visual effects applied to video in ShowVideo().     **/
770 /*************************************************************/
GTKSetEffects(int NewEffects)771 void GTKSetEffects(int NewEffects)
772 {
773   /* Will be redrawing whole screen next time */
774   RedrawAll=1;
775 
776   /* If virtual keyboard being turned off, release key */
777   if((Effects^NewEffects)&Effects&EFF_VKBD) VKbdNewKey(0);
778 
779   /* If full-screen/window modes being toggled... */
780   if(Widget&&((Effects^NewEffects)&NewEffects&(EFF_FULLSCR|EFF_WINDOWED)))
781   {
782     /* Toggle main widget mode */
783     if(NewEffects&EFF_FULLSCR)
784     {
785       FullScreen=1;
786       gdk_window_fullscreen(gtk_widget_get_parent_window(Widget));
787     }
788     else if(NewEffects&EFF_WINDOWED)
789     {
790       FullScreen=0;
791       gdk_window_unfullscreen(gtk_widget_get_parent_window(Widget));
792     }
793     /* Recreate output image buffer */
794     GTKResizeVideo(OutWidth,OutHeight);
795   }
796 
797   /* Set new effects */
798   Effects=NewEffects;
799 }
800 
801 /** GTKQuitHandler() *****************************************/
802 /** Process application quit.                               **/
803 /*************************************************************/
GTKQuitHandler(void)804 void GTKQuitHandler(void)
805 {
806   GdkEventKey Event;
807 
808   /* We are quitting */
809   VideoImg     = 0;
810   FocusOUT     = 0;
811   /* Simulate F12 (quit) keypress */
812   Event.type   = GDK_KEY_PRESS;
813   Event.keyval = GDK_F12;
814   GTKKeyHandler(Widget,&Event,0);
815 }
816 
817 /** GTKMenuHandler() *****************************************/
818 /** Forward user menu selections to keyboard handler, as    **/
819 /** keys.                                                   **/
820 /*************************************************************/
GTKMenuHandler(GtkAction * Action,int CmdID)821 void GTKMenuHandler(GtkAction *Action,int CmdID)
822 {
823   /* If key handler installed, feed it with menu command ID */
824   if(KeyHandler) (*KeyHandler)((CmdID&CON_KEYCODE)|KeyModes);
825   /* Send key press */
826   LastKey = CmdID&CON_KEYCODE;
827 }
828 
829 /** GTKFocusHandler() ****************************************/
830 /** Process focus events.                                   **/
831 /*************************************************************/
GTKFocusHandler(GtkWidget * SrcWidget,GdkEventFocus * Event,gpointer Arg)832 gint GTKFocusHandler(GtkWidget *SrcWidget,GdkEventFocus *Event,gpointer Arg)
833 {
834   static int J;
835 
836   if(Event->in)
837   {
838     /* When focus was out... */
839     if(FocusOUT)
840     {
841       /* Audio back on */
842       PauseAudio(0);
843       SetChannels(MasterVolume,J);
844       /* Focus is now in */
845       FocusOUT=0;
846     }
847   }
848   else
849   {
850     /* When save-CPU option enabled... */
851     if(Effects&EFF_SAVECPU)
852     {
853       /* Audio off */
854       J=MasterSwitch;
855       SetChannels(MasterVolume,0);
856       PauseAudio(1);
857       /* Focus is now out */
858       FocusOUT=1;
859     }
860   }
861 
862   /* Event handled */
863   return(TRUE);
864 }
865 
866 /** GTKPenHandler() ******************************************/
867 /** Process pen events.                                     **/
868 /*************************************************************/
GTKPenHandler(GtkWidget * SrcWidget,GdkEventCrossing * Event,gpointer Arg)869 gint GTKPenHandler(GtkWidget *SrcWidget,GdkEventCrossing *Event,gpointer Arg)
870 {
871   GdkEventMotion *ME;
872   GdkEventCrossing *CE;
873   int J,X,Y,W,H,Key;
874 
875   /* Simulate mouse button press/release, react to motion */
876   switch(Event->type)
877   {
878     default:
879       /* Event not handled */
880       return(FALSE);
881 
882     case GDK_MOTION_NOTIFY:
883       /* Need a mouse handler */
884       if(!MouseHandler) break;
885       /* Cast event type */
886       ME= (GdkEventMotion *)Event;
887       X = (int)(ME->x-Widget->allocation.x);
888       Y = (int)(ME->y-Widget->allocation.y);
889       /* Accommodate for hardware scaling */
890       if(IS_HWSCALED)
891       {
892         X /= FBImg.W&&VideoW? FBImg.W/VideoW:1;
893         Y /= FBImg.H&&VideoH? FBImg.H/VideoH:1;
894       }
895       /* Call mouse handler */
896       (*MouseHandler)(
897         MseState
898       | ((unsigned int)X&0xFFFF)
899       | (((unsigned int)Y&0x3FFF)<<16)
900       );
901       break;
902 
903     case GDK_ENTER_NOTIFY:
904       /* Mouse, joystick, dialpad buttons now released */
905       MseState&= ~0x40000000;
906       PenDigit = 0;
907       PenState = 0;
908       /* Virtual keyboard key now released */
909       VKbdNewKey(0);
910       break;
911 
912     case GDK_LEAVE_NOTIFY:
913       /* Mouse button now pressed */
914       MseState|=0x40000000;
915       /* Cast event type */
916       CE=(GdkEventCrossing *)Event;
917       /* Compute widget size and coordinates */
918       X = (int)(CE->x-Widget->allocation.x);
919       Y = (int)(CE->y-Widget->allocation.y);
920       W = Widget->allocation.width;
921       H = Widget->allocation.height;
922       /* Accommodate for hardware scaling */
923       if(IS_HWSCALED)
924       {
925         J=FBImg.W&&VideoW? FBImg.W/VideoW:1;X/=J;W/=J;
926         J=FBImg.H&&VideoH? FBImg.H/VideoH:1;Y/=J;H/=J;
927       }
928       /* Process virtual keyboard */
929       Key = Effects&EFF_VKBD? GenericPenKeyboard(X,Y,W,H):0;
930       VKbdNewKey(Key);
931       /* Process virtual joystick and dialpad */
932       if(!Key)
933       {
934         /* Get new pen joystick and dialpad states */
935         PenDigit = GenericPenDialpad(X,Y,W,H);
936         PenState = GenericPenJoystick(X,Y,W,H);
937         /* Generate keypresses from joystick */
938         switch(PenState)
939         {
940           case BTN_FIREB:
941           case BTN_EXIT:  LastKey=CON_EXIT;break;
942           case BTN_FIREA:
943           case BTN_START: LastKey=CON_OK;break;
944           case BTN_LEFT:  LastKey=CON_LEFT;break;
945           case BTN_RIGHT: LastKey=CON_RIGHT;break;
946           case BTN_UP:    LastKey=CON_UP;break;
947           case BTN_DOWN:  LastKey=CON_DOWN;break;
948         }
949       }
950       break;
951   }
952 
953   /* Event handled */
954   return(TRUE);
955 }
956 
957 /** GTKKeyHandler() ******************************************/
958 /** Process key presses/releases.                           **/
959 /*************************************************************/
GTKKeyHandler(GtkWidget * SrcWidget,GdkEventKey * Event,gpointer Arg)960 gint GTKKeyHandler(GtkWidget *SrcWidget,GdkEventKey *Event,gpointer Arg)
961 {
962   unsigned int J;
963   GdkWindow *W;
964 
965 //printf("KEY %s: keyval=%X, code=%X\n",
966 //Event->type==GDK_KEY_PRESS? "PRESS":Event->type==GDK_KEY_RELEASE? "RELEASE":"OTHER",
967 //Event->keyval,Event->hardware_keycode
968 //);
969 
970   /* If key pressed... */
971   if(Event->type==GDK_KEY_PRESS)
972   {
973     /* Special keys pressed... */
974     switch(J=Event->keyval)
975     {
976       case GDK_Left:         JoyState|=BTN_LEFT;LastKey=CON_LEFT;break;
977       case GDK_Right:        JoyState|=BTN_RIGHT;LastKey=CON_RIGHT;break;
978       case GDK_Up:           JoyState|=BTN_UP;LastKey=CON_UP;break;
979       case GDK_Down:         JoyState|=BTN_DOWN;LastKey=CON_DOWN;break;
980       case GDK_Shift_L:
981       case GDK_Shift_R:      KeyModes|=CON_SHIFT;break;
982       case GDK_Multi_key:
983       case GDK_Alt_L:
984       case GDK_Alt_R:        KeyModes|=CON_ALT;break;
985       case GDK_Control_L:
986       case GDK_Control_R:    KeyModes|=CON_CONTROL;break;
987       case GDK_Caps_Lock:    KeyModes|=CON_CAPS;break;
988       case GDK_Escape:       JoyState|=BTN_EXIT;LastKey=CON_EXIT;break;
989       case ',':
990       case GDK_Tab:          JoyState|=BTN_SELECT;break;
991       case GDK_BackSpace:    LastKey=8;break;
992       case '.':
993       case GDK_KP_Enter:     JoyState|=BTN_START;LastKey=CON_OK;break;
994       case GDK_F4:           LastKey=CON_MENU;break;
995 
996       case GDK_Return:
997         /* This is DPad center, have to use modifiers */
998         /* to protect against accidental keypresses   */
999         if(KeyModes&(CON_CONTROL|CON_SHIFT|CON_ALT)) JoyState|=BTN_START;
1000         LastKey=CON_OK;
1001         break;
1002 
1003       case 'q': case 'e': case 't': case 'u': case 'o':
1004         JoyState|=BTN_FIREL;break;
1005       case 'w': case 'r': case 'y': case 'i': case 'p':
1006         JoyState|=BTN_FIRER;break;
1007       case 'a': case 's': case 'd': case 'f': case 'g':
1008       case 'h': case 'j': case 'k': case 'l': case ' ':
1009         JoyState|=BTN_FIREA;break;
1010       case 'z': case 'x': case 'c': case 'v': case 'b':
1011       case 'n': case 'm':
1012         JoyState|=BTN_FIREB;break;
1013 
1014       case GDK_F6: /* Maemo FullScreen */
1015          /* When certain mode forced, do not toggle */
1016          if(Effects&(EFF_FULLSCR|EFF_WINDOWED)) break;
1017          /* Get main widget */
1018          W=gtk_widget_get_parent_window(Widget);
1019          /* Toggle full-screen mode */
1020          FullScreen=!FullScreen;
1021          if(FullScreen) gdk_window_fullscreen(W);
1022          else           gdk_window_unfullscreen(W);
1023          /* Automatically resize video output */
1024          GTKResizeVideo(OutWidth,OutHeight);
1025          break;
1026 
1027       case GDK_F7: /* Maemo VolumeUP */
1028         /* Volume up */
1029         if(Effects&EFF_NOVOLUME) LastKey=CON_VOLUP;
1030         else SetChannels(MasterVolume<247? MasterVolume+8:255,MasterSwitch);
1031         break;
1032 
1033       case GDK_F8: /* Maemo VolumeDOWN */
1034         /* Volume down */
1035         if(Effects&EFF_NOVOLUME) LastKey=CON_VOLDOWN;
1036         else SetChannels(MasterVolume>8? MasterVolume-8:0,MasterSwitch);
1037         break;
1038 
1039       case GDK_F12: /* Quit application */
1040         /* Remove video buffer to let EMULib know we are quitting */
1041         VideoImg=0;
1042         break;
1043     }
1044 
1045     /* Process ASCII keys */
1046     if((J>=' ')&&(J<0x7F)) LastKey=toupper(J);
1047     /* Call user handler */
1048     if(J&&KeyHandler) (*KeyHandler)(J|KeyModes);
1049   }
1050 
1051   /* If key released... */
1052   if(Event->type==GDK_KEY_RELEASE)
1053   {
1054     /* Special keys released... */
1055     switch(J=Event->keyval)
1056     {
1057       case GDK_Left:         JoyState&=~BTN_LEFT;break;
1058       case GDK_Right:        JoyState&=~BTN_RIGHT;break;
1059       case GDK_Up:           JoyState&=~BTN_UP;break;
1060       case GDK_Down:         JoyState&=~BTN_DOWN;break;
1061       case GDK_Shift_L:
1062       case GDK_Shift_R:      KeyModes&=~CON_SHIFT;break;
1063       case GDK_Multi_key:
1064       case GDK_Alt_L:
1065       case GDK_Alt_R:        KeyModes&=~CON_ALT;break;
1066       case GDK_Control_L:
1067       case GDK_Control_R:    KeyModes&=~CON_CONTROL;break;
1068       case GDK_Caps_Lock:    KeyModes&=~CON_CAPS;break;
1069       case GDK_Escape:       JoyState&=~BTN_EXIT;break;
1070       case ',':
1071       case GDK_Tab:          JoyState&=~BTN_SELECT;break;
1072       case '.':
1073       case GDK_Return:
1074       case GDK_KP_Enter:     JoyState&=~BTN_START;break;
1075 
1076       case 'q': case 'e': case 't': case 'u': case 'o':
1077         JoyState&=~BTN_FIREL;break;
1078       case 'w': case 'r': case 'y': case 'i': case 'p':
1079         JoyState&=~BTN_FIRER;break;
1080       case 'a': case 's': case 'd': case 'f': case 'g':
1081       case 'h': case 'j': case 'k': case 'l': case ' ':
1082         JoyState&=~BTN_FIREA;break;
1083       case 'z': case 'x': case 'c': case 'v': case 'b':
1084       case 'n': case 'm':
1085         JoyState&=~BTN_FIREB;break;
1086     }
1087 
1088     /* Call user handler */
1089     if(J&&KeyHandler) (*KeyHandler)(J|CON_RELEASE|KeyModes);
1090   }
1091 
1092   /* Event handled */
1093   return(TRUE);
1094 }
1095 
1096 /** GTKAddMenu() *********************************************/
1097 /** Add a menu with a command ID.                           **/
1098 /*************************************************************/
GTKAddMenu(const char * Label,int CmdID)1099 int GTKAddMenu(const char *Label,int CmdID)
1100 {
1101   GtkWidget *Item;
1102 
1103   if(!Menu) return(0);
1104   Item = Label? gtk_menu_item_new_with_label(Label)
1105               : gtk_separator_menu_item_new();
1106   if(!Item) return(0);
1107   gtk_menu_insert(Menu,Item,g_list_length(GTK_MENU_SHELL(Menu)->children)-1);
1108   g_signal_connect(G_OBJECT(Item),"activate",G_CALLBACK(GTKMenuHandler),(void *) CmdID);
1109   gtk_widget_show_all(GTK_WIDGET(Menu));
1110   return(1);
1111 }
1112 
1113 /** GTKAskFilename() *****************************************/
1114 /** Ask user to select a file and return the file name, or  **/
1115 /** 0 on cancel.                                            **/
1116 /*************************************************************/
GTKAskFilename(GtkFileChooserAction Action)1117 const char *GTKAskFilename(GtkFileChooserAction Action)
1118 {
1119   gchar *FileName,*PathName;
1120   GtkWidget *Dialog;
1121 
1122   /* Must have a window */
1123   if(!HWnd) return(0);
1124 
1125   /* Create file selection dialog */
1126   Dialog = hildon_file_chooser_dialog_new(GTK_WINDOW(HWnd),Action);
1127   if(!Dialog) return(0);
1128 
1129   /* Set initial path */
1130   PathName = (gchar *)GTKGetString("path");
1131   if(PathName)
1132   {
1133     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(Dialog),PathName);
1134     g_free(PathName);
1135   }
1136 
1137   /* Show and run dialog */
1138   gtk_widget_show_all(GTK_WIDGET(Dialog));
1139   FileName = gtk_dialog_run(GTK_DIALOG(Dialog))==GTK_RESPONSE_OK?
1140     gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(Dialog)):0;
1141 
1142   /* If selection made, memorize path for the next time */
1143   if(FileName)
1144   {
1145     PathName = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(Dialog));
1146     if(PathName) { GTKSetString("path",PathName);g_free(PathName); }
1147   }
1148 
1149   /* Done, close the widget and let it get processed */
1150   gtk_widget_destroy(Dialog);
1151   GTKProcessEvents();
1152   return(FileName);
1153 }
1154 
1155 /** GTKMessage() *********************************************/
1156 /** Show text message to the user.                          **/
1157 /*************************************************************/
GTKMessage(const char * Text)1158 void GTKMessage(const char *Text)
1159 {
1160   GtkWidget *W;
1161 
1162   /* Create message widget */
1163   if(!HWnd||!(W=hildon_note_new_information(GTK_WINDOW(HWnd),Text))) return;
1164   /* Show message */
1165   gtk_dialog_run(GTK_DIALOG(W));
1166   gtk_widget_destroy(W);
1167 }
1168 
1169 /** DBusHandler() ********************************************/
1170 /** Process DBus messages.                                  **/
1171 /*************************************************************/
DBusHandler(const gchar * Interface,const gchar * Method,GArray * Args,gpointer Data,osso_rpc_t * Result)1172 gint DBusHandler(const gchar *Interface,const gchar *Method,GArray *Args,gpointer Data,osso_rpc_t *Result)
1173 {
1174   const char *Hex = "0123456789ABCDEF";
1175   osso_rpc_t V;
1176   char *S,*D,*P,*T;
1177 
1178   if(Method&&Args&&!strcmp(Method,"mime_open"))
1179   {
1180     /* Get first argument (filename) */
1181     V = g_array_index(Args,osso_rpc_t,0);
1182 
1183     /* If filename valid and belongs to local filesystem... */
1184     if((V.type==DBUS_TYPE_STRING)&&V.value.s&&!memcmp(V.value.s,"file://",7))
1185     {
1186       /* Convert name to plain ASCII */
1187       for(D=V.value.s,S=D+7;*S;)
1188         if(S[0]!='%') *D++=*S++;
1189         else
1190         {
1191           P = strchr(Hex,toupper(S[1]));
1192           T = P? strchr(Hex,toupper(S[2])):0;
1193           if(!P||!T) *D++=*S++; else { *D++=((P-Hex)<<4)+(T-Hex);S+=3; }
1194         }
1195       *D='\0';
1196 
1197       /* If file handler present, call it */
1198       if(FileHandler&&(*FileHandler)(V.value.s))
1199       {
1200         /* Bring application to front */
1201         if(HWnd) gtk_window_present(GTK_WINDOW(HWnd));
1202         /* Successfully done with the message */
1203         Result->type    = DBUS_TYPE_BOOLEAN;
1204         Result->value.b = TRUE;
1205         return(OSSO_OK);
1206       }
1207     }
1208 
1209     /* Message processing failed */
1210     return(OSSO_ERROR);
1211   }
1212 
1213   /* Message not recognized */
1214   return(DBUS_TYPE_INVALID);
1215 }
1216 
1217 /** GTKResizeVideo() *****************************************/
1218 /** Change output rectangle dimensions.                     **/
1219 /*************************************************************/
GTKResizeVideo(int Width,int Height)1220 int GTKResizeVideo(int Width,int Height)
1221 {
1222   int SX,SY,NX,NY,GX,GY;
1223   Image *Img;
1224 
1225   /* Will be redrawing whole screen a few times */
1226   RedrawAll = 1;
1227 
1228   /* Memorize new output size preferences */
1229   OutWidth  = Width;
1230   OutHeight = Height;
1231 
1232   /* Compute new GTK widget size */
1233   GX = FullScreen? SCREEN_W:720;
1234   GY = FullScreen? SCREEN_H:448;
1235 
1236   /* Automatically compute width, if requested */
1237   if(Width>0)
1238   {
1239     SX = VideoImg&&(VideoW>0)? VideoW:Width;
1240     NX = (Width+SX-1)/SX;
1241   }
1242   else
1243   {
1244     SX = VideoImg&&(VideoW>0)&&(VideoW<GX)? VideoW:GX;
1245     for(NX=1;SX*(NX+1)<=GX;++NX);
1246   }
1247 
1248   /* Automatically compute height, if requested */
1249   if(Height>0)
1250   {
1251     SY = VideoImg&&(VideoH>0)? VideoH:Height;
1252     NY = (Height+SY-1)/SY;
1253   }
1254   else
1255   {
1256     SY = VideoImg&&(VideoH>0)&&(VideoH<GY)? VideoH:GY;
1257     for(NY=1;SY*(NY+1)<=GY;++NY);
1258   }
1259 
1260   /* Make sure automatically sized image is proportional */
1261   if(NX>NY) NX=NY; else NY=NX;
1262   if(Width<=0)  Width=SX*NX;
1263   if(Height<=0) Height=SY*NY;
1264 
1265   /* Crop new subimage for the output */
1266   Img = FullScreen&&(FBFD>=0)&&FBImg.Data? &FBImg:&GTKImg;
1267   CropImage(&OutImg,Img,(Img->W-Width)>>1,(Img->H-Height)>>1,Width,Height);
1268 
1269   /* Done */
1270   return(1);
1271 }
1272 
1273 /** SetFileHandler() *****************************************/
1274 /** Attach handler that will be called to open files.       **/
1275 /*************************************************************/
SetFileHandler(int (* Handler)(const char * Filename))1276 void SetFileHandler(int (*Handler)(const char *Filename))
1277 {
1278   int J;
1279 
1280   /* Set new DBus open-file message handler */
1281   FileHandler = Handler;
1282 
1283   /* Spin GTK loop to let pending DBus messages come through */
1284   if(Handler)
1285     for(J=0;J<20;++J) { gtk_main_iteration_do(FALSE);usleep(20); }
1286 }
1287 
1288 /** SetMouseHandler() ****************************************/
1289 /** Attach handler that will be called on mouse movement.   **/
1290 /*************************************************************/
SetMouseHandler(void (* Handler)(unsigned int MouseState))1291 void SetMouseHandler(void (*Handler)(unsigned int MouseState))
1292 {
1293   gint Events;
1294 
1295   /* Set new mouse movement handler */
1296   MouseHandler = Handler;
1297   /* Enable motion events if setting valid handler */
1298   if(HWnd)
1299   {
1300     Events = gtk_widget_get_events(GTK_WIDGET(HWnd));
1301     if(Handler) Events|=GDK_POINTER_MOTION_MASK;
1302     else        Events&=~GDK_POINTER_MOTION_MASK;
1303     gtk_widget_set_events(GTK_WIDGET(HWnd),Events);
1304   }
1305 }
1306 
1307 /** PenJoystick() ********************************************/
1308 /** Get simulated joystick buttons from touch screen UI.    **/
1309 /** Result compatible with GetJoystick().                   **/
1310 /*************************************************************/
PenJoystick(void)1311 unsigned int PenJoystick(void)
1312 {
1313   /* Process any accumulated keypresses, etc. */
1314   GTKProcessEvents();
1315   /* Return current virtual joystick state */
1316   return(PenState);
1317 }
1318 
1319 /** PenDialpad() *********************************************/
1320 /** Get simulated dialpad buttons from touch screen UI.     **/
1321 /*************************************************************/
PenDialpad(void)1322 unsigned char PenDialpad(void)
1323 {
1324   /* Process any accumulated keypresses, etc. */
1325   GTKProcessEvents();
1326   /* Return current virtual dialpad state */
1327   return(PenDigit);
1328 }
1329 
1330 /** GTKGet*()/GTKSet*() **************************************/
1331 /** Wrappers for getting and setting GConf config values.   **/
1332 /*************************************************************/
GTKGetString(const char * Key)1333 const char *GTKGetString(const char *Key)
1334 {
1335   char *P,*Path,*Value;
1336 
1337   /* Allocate buffer for config path */
1338   P    = strrchr(ARGV[0],'/');
1339   P    = P? P+1:ARGV[0];
1340   Path = malloc(strlen(P)+strlen(Key)+8);
1341   if(!Path) return(0);
1342 
1343   /* Compose config path */
1344   strcpy(Path,"/apps/");
1345   strcat(Path,P);
1346   strcat(Path,"/");
1347   strcat(Path,Key);
1348 
1349   /* Get and return value */
1350   Value=gconf_client_get_string(Conf,Path,0);
1351   free(Path);
1352   return(Value);
1353 }
1354 
GTKGetInteger(const char * Key,unsigned int Default)1355 unsigned int GTKGetInteger(const char *Key,unsigned int Default)
1356 {
1357   char *P,*Path;
1358   GConfValue *CfgValue;
1359   gint Value;
1360 
1361   /* Allocate buffer for config path */
1362   P    = strrchr(ARGV[0],'/');
1363   P    = P? P+1:ARGV[0];
1364   Path = malloc(strlen(P)+strlen(Key)+8);
1365   if(!Path) return(0);
1366 
1367   /* Compose config path */
1368   strcpy(Path,"/apps/");
1369   strcat(Path,P);
1370   strcat(Path,"/");
1371   strcat(Path,Key);
1372 
1373   /* Get and verify value */
1374   CfgValue = gconf_client_get_without_default(Conf,Path,0);
1375   Value    = CfgValue&&(CfgValue->type==GCONF_VALUE_INT)?
1376     gconf_value_get_int(CfgValue) : Default;
1377 
1378   /* Done */
1379   if(CfgValue) gconf_value_free(CfgValue);
1380   free(Path);
1381   return(Value);
1382 }
1383 
GTKSetString(const char * Key,const char * Value)1384 int GTKSetString(const char *Key,const char *Value)
1385 {
1386   char *P,*Path;
1387   int Result;
1388 
1389   /* Allocate buffer for config path */
1390   P    = strrchr(ARGV[0],'/');
1391   P    = P? P+1:ARGV[0];
1392   Path = malloc(strlen(P)+strlen(Key)+8);
1393   if(!Path) return(0);
1394 
1395   /* Compose config path */
1396   strcpy(Path,"/apps/");
1397   strcat(Path,P);
1398   strcat(Path,"/");
1399   strcat(Path,Key);
1400 
1401   /* Set value */
1402   Result=gconf_client_set_string(Conf,Path,Value,0);
1403   free(Path);
1404   return(Result);
1405 }
1406 
GTKSetInteger(const char * Key,unsigned int Value)1407 int GTKSetInteger(const char *Key,unsigned int Value)
1408 {
1409   char *P,*Path;
1410   int Result;
1411 
1412   /* Allocate buffer for config path */
1413   P    = strrchr(ARGV[0],'/');
1414   P    = P? P+1:ARGV[0];
1415   Path = malloc(strlen(P)+strlen(Key)+8);
1416   if(!Path) return(0);
1417 
1418   /* Compose config path */
1419   strcpy(Path,"/apps/");
1420   strcat(Path,P);
1421   strcat(Path,"/");
1422   strcat(Path,Key);
1423 
1424   /* Set value */
1425   Result=gconf_client_set_int(Conf,Path,Value,0);
1426   free(Path);
1427   return(Result);
1428 }
1429