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(>KImg,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(>KImg);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(>KImg);
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:>KImg,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,>KImg,(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:>KImg;
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