1 /*++ @file
2 
3 Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>
4 Portions copyright (c) 2008 - 2011, Apple Inc. All rights reserved.<BR>
5 
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution.  The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10 
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include "Host.h"
17 
18 #include <sys/ipc.h>
19 #include <sys/shm.h>
20 
21 #include <X11/Xlib.h>
22 #include <X11/Xutil.h>
23 #include <X11/Xos.h>
24 #include <X11/extensions/XShm.h>
25 #include <X11/keysym.h>
26 #include <X11/cursorfont.h>
27 
28 #define KEYSYM_LOWER  0
29 #define KEYSYM_UPPER  1
30 
31 
32 struct uga_drv_shift_mask {
33   unsigned char shift;
34   unsigned char size;
35   unsigned char csize;
36 };
37 
38 #define NBR_KEYS 32
39 typedef struct {
40   EMU_GRAPHICS_WINDOW_PROTOCOL GraphicsIo;
41 
42   Display     *display;
43   int         screen;      // values for window_size in main
44   Window      win;
45   GC          gc;
46   Visual      *visual;
47 
48   int           depth;
49   unsigned int  width;
50   unsigned int  height;
51   unsigned int  line_bytes;
52   unsigned int  pixel_shift;
53   unsigned char *image_data;
54 
55   struct uga_drv_shift_mask r, g, b;
56 
57   int             use_shm;
58   XShmSegmentInfo xshm_info;
59   XImage          *image;
60   char            *Title;
61 
62   unsigned int key_rd;
63   unsigned int key_wr;
64   unsigned int key_count;
65   EFI_KEY_DATA keys[NBR_KEYS];
66 
67   EFI_KEY_STATE KeyState;
68 
69   EMU_GRAPHICS_WINDOW_REGISTER_KEY_NOTIFY_CALLBACK    MakeRegisterdKeyCallback;
70   EMU_GRAPHICS_WINDOW_REGISTER_KEY_NOTIFY_CALLBACK    BreakRegisterdKeyCallback;
71   VOID                                                *RegisterdKeyCallbackContext;
72 
73   int                        previous_x;
74   int                        previous_y;
75   EFI_SIMPLE_POINTER_STATE   pointer_state;
76   int                        pointer_state_changed;
77 } GRAPHICS_IO_PRIVATE;
78 
79 void
80 HandleEvents(
81   IN GRAPHICS_IO_PRIVATE *Drv
82   );
83 
84 void
fill_shift_mask(IN struct uga_drv_shift_mask * sm,IN unsigned long mask)85 fill_shift_mask (
86   IN  struct uga_drv_shift_mask *sm,
87   IN  unsigned long             mask
88   )
89 {
90   sm->shift = 0;
91   sm->size = 0;
92   while ((mask & 1) == 0) {
93     mask >>= 1;
94     sm->shift++;
95   }
96   while (mask & 1) {
97     sm->size++;
98     mask >>= 1;
99   }
100   sm->csize = 8 - sm->size;
101 }
102 
103 int
TryCreateShmImage(IN GRAPHICS_IO_PRIVATE * Drv)104 TryCreateShmImage (
105   IN  GRAPHICS_IO_PRIVATE *Drv
106   )
107 {
108   Drv->image = XShmCreateImage (
109                  Drv->display, Drv->visual,
110                  Drv->depth, ZPixmap, NULL, &Drv->xshm_info,
111                  Drv->width, Drv->height
112                  );
113   if (Drv->image == NULL) {
114     return 0;
115   }
116 
117   switch (Drv->image->bitmap_unit) {
118   case 32:
119     Drv->pixel_shift = 2;
120     break;
121   case 16:
122     Drv->pixel_shift = 1;
123     break;
124   case 8:
125     Drv->pixel_shift = 0;
126     break;
127   }
128 
129   Drv->xshm_info.shmid = shmget (
130                           IPC_PRIVATE, Drv->image->bytes_per_line * Drv->image->height,
131                           IPC_CREAT | 0777
132                           );
133   if (Drv->xshm_info.shmid < 0) {
134     XDestroyImage(Drv->image);
135     return 0;
136   }
137 
138   Drv->image_data = shmat (Drv->xshm_info.shmid, NULL, 0);
139   if(!Drv->image_data) {
140     shmctl (Drv->xshm_info.shmid, IPC_RMID, NULL);
141     XDestroyImage(Drv->image);
142     return 0;
143   }
144 
145 #ifndef __APPLE__
146   //
147   // This closes shared memory in real time on OS X. Only closes after folks quit using
148   // it on Linux.
149   //
150   shmctl (Drv->xshm_info.shmid, IPC_RMID, NULL);
151 #endif
152 
153   Drv->xshm_info.shmaddr = (char*)Drv->image_data;
154   Drv->image->data = (char*)Drv->image_data;
155 
156   if (!XShmAttach (Drv->display, &Drv->xshm_info)) {
157     shmdt (Drv->image_data);
158     XDestroyImage(Drv->image);
159     return 0;
160   }
161   return 1;
162 }
163 
164 
165 EFI_STATUS
X11Size(IN EMU_GRAPHICS_WINDOW_PROTOCOL * GraphicsIo,IN UINT32 Width,IN UINT32 Height)166 X11Size (
167   IN  EMU_GRAPHICS_WINDOW_PROTOCOL  *GraphicsIo,
168   IN  UINT32                        Width,
169   IN  UINT32                        Height
170   )
171 {
172   GRAPHICS_IO_PRIVATE *Drv;
173   XSizeHints          size_hints;
174 
175   // Destroy current buffer if created.
176   Drv = (GRAPHICS_IO_PRIVATE *)GraphicsIo;
177   if (Drv->image != NULL) {
178     // Before destroy buffer, need to make sure the buffer available for access.
179     XDestroyImage (Drv->image);
180 
181     if (Drv->use_shm) {
182       shmdt (Drv->image_data);
183     }
184 
185     Drv->image_data = NULL;
186     Drv->image = NULL;
187   }
188 
189   Drv->width = Width;
190   Drv->height = Height;
191   XResizeWindow (Drv->display, Drv->win, Width, Height);
192 
193   // Allocate image.
194   if (XShmQueryExtension(Drv->display) && TryCreateShmImage(Drv)) {
195     Drv->use_shm = 1;
196   } else {
197     Drv->use_shm = 0;
198     if (Drv->depth > 16) {
199       Drv->pixel_shift = 2;
200     } else if (Drv->depth > 8) {
201       Drv->pixel_shift = 1;
202     } else {
203       Drv->pixel_shift = 0;
204     }
205 
206     Drv->image_data = malloc ((Drv->width * Drv->height) << Drv->pixel_shift);
207     Drv->image = XCreateImage (
208                     Drv->display, Drv->visual, Drv->depth,
209                     ZPixmap, 0, (char *)Drv->image_data,
210                     Drv->width, Drv->height,
211                     8 << Drv->pixel_shift, 0
212                     );
213   }
214 
215   Drv->line_bytes = Drv->image->bytes_per_line;
216 
217   fill_shift_mask (&Drv->r, Drv->image->red_mask);
218   fill_shift_mask (&Drv->g, Drv->image->green_mask);
219   fill_shift_mask (&Drv->b, Drv->image->blue_mask);
220 
221   // Set WM hints.
222   size_hints.flags = PSize | PMinSize | PMaxSize;
223   size_hints.min_width = size_hints.max_width = size_hints.base_width = Width;
224   size_hints.min_height = size_hints.max_height = size_hints.base_height = Height;
225   XSetWMNormalHints (Drv->display, Drv->win, &size_hints);
226 
227   XMapWindow (Drv->display, Drv->win);
228   HandleEvents (Drv);
229   return EFI_SUCCESS;
230 }
231 
232 void
handleKeyEvent(IN GRAPHICS_IO_PRIVATE * Drv,IN XEvent * ev,IN BOOLEAN Make)233 handleKeyEvent (
234   IN  GRAPHICS_IO_PRIVATE *Drv,
235   IN  XEvent              *ev,
236   IN  BOOLEAN             Make
237   )
238 {
239   KeySym        *KeySym;
240   EFI_KEY_DATA  KeyData;
241   int           KeySymArraySize;
242 
243   if (Make) {
244     if (Drv->key_count == NBR_KEYS) {
245       return;
246     }
247   }
248 
249   // keycode is a physical key on the keyboard
250   // KeySym is a mapping of a physical key
251   // KeyboardMapping is the array of KeySym for a given keycode. key, shifted key, option key, command key, ...
252   //
253   // Returns an array of KeySymArraySize of KeySym for the keycode. [0] is lower case, [1] is upper case,
254   // [2] and [3] are based on option and command modifiers. The problem we have is command V
255   // could be mapped to a crazy Unicode character so the old scheme of returning a string.
256   //
257   KeySym = XGetKeyboardMapping (Drv->display, ev->xkey.keycode, 1, &KeySymArraySize);
258 
259   KeyData.Key.ScanCode = 0;
260   KeyData.Key.UnicodeChar = 0;
261   KeyData.KeyState.KeyShiftState = 0;
262 
263   //
264   // Skipping EFI_SCROLL_LOCK_ACTIVE & EFI_NUM_LOCK_ACTIVE since they are not on Macs
265   //
266   if ((ev->xkey.state & LockMask) == 0) {
267     Drv->KeyState.KeyToggleState &= ~EFI_CAPS_LOCK_ACTIVE;
268   } else {
269     if (Make) {
270       Drv->KeyState.KeyToggleState |= EFI_CAPS_LOCK_ACTIVE;
271     }
272   }
273 
274   // Skipping EFI_MENU_KEY_PRESSED and EFI_SYS_REQ_PRESSED
275 
276   switch (*KeySym) {
277   case XK_Control_R:
278     if (Make) {
279       Drv->KeyState.KeyShiftState |=  EFI_RIGHT_CONTROL_PRESSED;
280     } else {
281       Drv->KeyState.KeyShiftState &= ~EFI_RIGHT_CONTROL_PRESSED;
282     }
283    break;
284   case XK_Control_L:
285     if (Make) {
286       Drv->KeyState.KeyShiftState |=  EFI_LEFT_CONTROL_PRESSED;
287     } else {
288       Drv->KeyState.KeyShiftState &= ~EFI_LEFT_CONTROL_PRESSED;
289     }
290     break;
291 
292   case XK_Shift_R:
293     if (Make) {
294       Drv->KeyState.KeyShiftState |=  EFI_RIGHT_SHIFT_PRESSED;
295     } else {
296       Drv->KeyState.KeyShiftState &= ~EFI_RIGHT_SHIFT_PRESSED;
297     }
298     break;
299   case XK_Shift_L:
300     if (Make) {
301       Drv->KeyState.KeyShiftState |=  EFI_LEFT_SHIFT_PRESSED;
302     } else {
303       Drv->KeyState.KeyShiftState &= ~EFI_LEFT_SHIFT_PRESSED;
304     }
305     break;
306 
307   case XK_Mode_switch:
308     if (Make) {
309       Drv->KeyState.KeyShiftState |=  EFI_LEFT_ALT_PRESSED;
310     } else {
311       Drv->KeyState.KeyShiftState &= ~EFI_LEFT_ALT_PRESSED;
312     }
313     break;
314 
315   case XK_Meta_R:
316     if (Make) {
317       Drv->KeyState.KeyShiftState |=  EFI_RIGHT_LOGO_PRESSED;
318     } else {
319       Drv->KeyState.KeyShiftState &= ~EFI_RIGHT_LOGO_PRESSED;
320     }
321     break;
322   case XK_Meta_L:
323     if (Make) {
324       Drv->KeyState.KeyShiftState |=  EFI_LEFT_LOGO_PRESSED;
325     } else {
326       Drv->KeyState.KeyShiftState &= ~EFI_LEFT_LOGO_PRESSED;
327     }
328     break;
329 
330   case XK_KP_Home:
331   case XK_Home:       KeyData.Key.ScanCode = SCAN_HOME;       break;
332 
333   case XK_KP_End:
334   case XK_End:        KeyData.Key.ScanCode = SCAN_END;        break;
335 
336   case XK_KP_Left:
337   case XK_Left:       KeyData.Key.ScanCode = SCAN_LEFT;       break;
338 
339   case XK_KP_Right:
340   case XK_Right:      KeyData.Key.ScanCode = SCAN_RIGHT;      break;
341 
342   case XK_KP_Up:
343   case XK_Up:         KeyData.Key.ScanCode = SCAN_UP;         break;
344 
345   case XK_KP_Down:
346   case XK_Down:       KeyData.Key.ScanCode = SCAN_DOWN;       break;
347 
348   case XK_KP_Delete:
349   case XK_Delete:       KeyData.Key.ScanCode = SCAN_DELETE;     break;
350 
351   case XK_KP_Insert:
352   case XK_Insert:     KeyData.Key.ScanCode = SCAN_INSERT;     break;
353 
354   case XK_KP_Page_Up:
355   case XK_Page_Up:    KeyData.Key.ScanCode = SCAN_PAGE_UP;    break;
356 
357   case XK_KP_Page_Down:
358   case XK_Page_Down:  KeyData.Key.ScanCode = SCAN_PAGE_DOWN;  break;
359 
360   case XK_Escape:     KeyData.Key.ScanCode = SCAN_ESC;        break;
361 
362   case XK_Pause:      KeyData.Key.ScanCode = SCAN_PAUSE;      break;
363 
364   case XK_KP_F1:
365   case XK_F1:   KeyData.Key.ScanCode = SCAN_F1;   break;
366 
367   case XK_KP_F2:
368   case XK_F2:   KeyData.Key.ScanCode = SCAN_F2;   break;
369 
370   case XK_KP_F3:
371   case XK_F3:   KeyData.Key.ScanCode = SCAN_F3;   break;
372 
373   case XK_KP_F4:
374   case XK_F4:   KeyData.Key.ScanCode = SCAN_F4;   break;
375 
376   case XK_F5:   KeyData.Key.ScanCode = SCAN_F5;   break;
377   case XK_F6:   KeyData.Key.ScanCode = SCAN_F6;   break;
378   case XK_F7:   KeyData.Key.ScanCode = SCAN_F7;   break;
379 
380   // Don't map into X11 by default on a Mac
381   // System Preferences->Keyboard->Keyboard Shortcuts can be configured
382   // to not use higher function keys as shortcuts and the will show up
383   // in X11.
384   case XK_F8:   KeyData.Key.ScanCode = SCAN_F8;   break;
385   case XK_F9:   KeyData.Key.ScanCode = SCAN_F9;   break;
386   case XK_F10:  KeyData.Key.ScanCode = SCAN_F10;  break;
387 
388   case XK_F11:  KeyData.Key.ScanCode = SCAN_F11;  break;
389   case XK_F12:  KeyData.Key.ScanCode = SCAN_F12;  break;
390 
391   case XK_F13:  KeyData.Key.ScanCode = SCAN_F13;  break;
392   case XK_F14:  KeyData.Key.ScanCode = SCAN_F14;  break;
393   case XK_F15:  KeyData.Key.ScanCode = SCAN_F15;  break;
394   case XK_F16:  KeyData.Key.ScanCode = SCAN_F16;  break;
395   case XK_F17:  KeyData.Key.ScanCode = SCAN_F17;  break;
396   case XK_F18:  KeyData.Key.ScanCode = SCAN_F18;  break;
397   case XK_F19:  KeyData.Key.ScanCode = SCAN_F19;  break;
398   case XK_F20:  KeyData.Key.ScanCode = SCAN_F20;  break;
399   case XK_F21:  KeyData.Key.ScanCode = SCAN_F21;  break;
400   case XK_F22:  KeyData.Key.ScanCode = SCAN_F22;  break;
401   case XK_F23:  KeyData.Key.ScanCode = SCAN_F23;  break;
402   case XK_F24:  KeyData.Key.ScanCode = SCAN_F24;  break;
403 
404   // No mapping in X11
405   //case XK_:   KeyData.Key.ScanCode = SCAN_MUTE;            break;
406   //case XK_:   KeyData.Key.ScanCode = SCAN_VOLUME_UP;       break;
407   //case XK_:   KeyData.Key.ScanCode = SCAN_VOLUME_DOWN;     break;
408   //case XK_:   KeyData.Key.ScanCode = SCAN_BRIGHTNESS_UP;   break;
409   //case XK_:   KeyData.Key.ScanCode = SCAN_BRIGHTNESS_DOWN; break;
410   //case XK_:   KeyData.Key.ScanCode = SCAN_SUSPEND;         break;
411   //case XK_:   KeyData.Key.ScanCode = SCAN_HIBERNATE;       break;
412   //case XK_:   KeyData.Key.ScanCode = SCAN_TOGGLE_DISPLAY;  break;
413   //case XK_:   KeyData.Key.ScanCode = SCAN_RECOVERY;        break;
414   //case XK_:   KeyData.Key.ScanCode = SCAN_EJECT;           break;
415 
416   case XK_BackSpace:  KeyData.Key.UnicodeChar = 0x0008; break;
417 
418   case XK_KP_Tab:
419   case XK_Tab:        KeyData.Key.UnicodeChar = 0x0009; break;
420 
421   case XK_Linefeed:   KeyData.Key.UnicodeChar = 0x000a; break;
422 
423   case XK_KP_Enter:
424   case XK_Return:     KeyData.Key.UnicodeChar = 0x000d; break;
425 
426   case XK_KP_Equal      : KeyData.Key.UnicodeChar = L'='; break;
427   case XK_KP_Multiply   : KeyData.Key.UnicodeChar = L'*'; break;
428   case XK_KP_Add        : KeyData.Key.UnicodeChar = L'+'; break;
429   case XK_KP_Separator  : KeyData.Key.UnicodeChar = L'~'; break;
430   case XK_KP_Subtract   : KeyData.Key.UnicodeChar = L'-'; break;
431   case XK_KP_Decimal    : KeyData.Key.UnicodeChar = L'.'; break;
432   case XK_KP_Divide     : KeyData.Key.UnicodeChar = L'/'; break;
433 
434   case XK_KP_0    : KeyData.Key.UnicodeChar = L'0'; break;
435   case XK_KP_1    : KeyData.Key.UnicodeChar = L'1'; break;
436   case XK_KP_2    : KeyData.Key.UnicodeChar = L'2'; break;
437   case XK_KP_3    : KeyData.Key.UnicodeChar = L'3'; break;
438   case XK_KP_4    : KeyData.Key.UnicodeChar = L'4'; break;
439   case XK_KP_5    : KeyData.Key.UnicodeChar = L'5'; break;
440   case XK_KP_6    : KeyData.Key.UnicodeChar = L'6'; break;
441   case XK_KP_7    : KeyData.Key.UnicodeChar = L'7'; break;
442   case XK_KP_8    : KeyData.Key.UnicodeChar = L'8'; break;
443   case XK_KP_9    : KeyData.Key.UnicodeChar = L'9'; break;
444 
445   default:
446     ;
447   }
448 
449   // The global state is our state
450   KeyData.KeyState.KeyShiftState = Drv->KeyState.KeyShiftState;
451   KeyData.KeyState.KeyToggleState = Drv->KeyState.KeyToggleState;
452 
453   if (*KeySym < XK_BackSpace) {
454     if (((Drv->KeyState.KeyShiftState & (EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED)) != 0) ||
455         ((Drv->KeyState.KeyToggleState & EFI_CAPS_LOCK_ACTIVE) != 0) ) {
456 
457       KeyData.Key.UnicodeChar = (CHAR16)KeySym[KEYSYM_UPPER];
458 
459       // Per UEFI spec since we converted the Unicode clear the shift bits we pass up
460       KeyData.KeyState.KeyShiftState &= ~(EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED);
461     } else {
462       KeyData.Key.UnicodeChar = (CHAR16)KeySym[KEYSYM_LOWER];
463     }
464   } else {
465     // XK_BackSpace is the start of XK_MISCELLANY. These are the XK_? keys we process in this file
466     ;
467   }
468 
469   if (Make) {
470     memcpy (&Drv->keys[Drv->key_wr], &KeyData, sizeof (EFI_KEY_DATA));
471     Drv->key_wr = (Drv->key_wr + 1) % NBR_KEYS;
472     Drv->key_count++;
473     if (Drv->MakeRegisterdKeyCallback != NULL) {
474       ReverseGasketUint64Uint64 (Drv->MakeRegisterdKeyCallback ,Drv->RegisterdKeyCallbackContext, &KeyData);
475     }
476   } else {
477     if (Drv->BreakRegisterdKeyCallback != NULL) {
478       ReverseGasketUint64Uint64 (Drv->BreakRegisterdKeyCallback ,Drv->RegisterdKeyCallbackContext, &KeyData);
479     }
480   }
481 }
482 
483 
484 void
handleMouseMoved(IN GRAPHICS_IO_PRIVATE * Drv,IN XEvent * ev)485 handleMouseMoved(
486   IN  GRAPHICS_IO_PRIVATE   *Drv,
487   IN  XEvent                *ev
488   )
489 {
490   if (ev->xmotion.x != Drv->previous_x) {
491     Drv->pointer_state.RelativeMovementX += ( ev->xmotion.x - Drv->previous_x );
492     Drv->previous_x = ev->xmotion.x;
493     Drv->pointer_state_changed = 1;
494   }
495 
496   if (ev->xmotion.y != Drv->previous_y) {
497     Drv->pointer_state.RelativeMovementY += ( ev->xmotion.y - Drv->previous_y );
498     Drv->previous_y = ev->xmotion.y;
499     Drv->pointer_state_changed = 1;
500   }
501 
502   Drv->pointer_state.RelativeMovementZ = 0;
503 }
504 
505 void
handleMouseDown(IN GRAPHICS_IO_PRIVATE * Drv,IN XEvent * ev,IN BOOLEAN Pressed)506 handleMouseDown (
507   IN  GRAPHICS_IO_PRIVATE *Drv,
508   IN  XEvent              *ev,
509   IN  BOOLEAN             Pressed
510   )
511 {
512   if (ev->xbutton.button == Button1) {
513     Drv->pointer_state_changed = (Drv->pointer_state.LeftButton != Pressed);
514     Drv->pointer_state.LeftButton = Pressed;
515   }
516   if ( ev->xbutton.button == Button2 ) {
517     Drv->pointer_state_changed = (Drv->pointer_state.RightButton != Pressed);
518     Drv->pointer_state.RightButton = Pressed;
519   }
520 }
521 
522 void
Redraw(IN GRAPHICS_IO_PRIVATE * Drv,IN UINTN X,IN UINTN Y,IN UINTN Width,IN UINTN Height)523 Redraw (
524   IN  GRAPHICS_IO_PRIVATE *Drv,
525   IN  UINTN               X,
526   IN  UINTN               Y,
527   IN  UINTN               Width,
528   IN  UINTN               Height
529   )
530 {
531   if (Drv->use_shm) {
532     XShmPutImage (
533       Drv->display, Drv->win, Drv->gc, Drv->image, X, Y, X, Y, Width, Height, False
534       );
535   } else {
536     XPutImage (
537       Drv->display, Drv->win, Drv->gc, Drv->image, X, Y, X, Y, Width, Height
538       );
539   }
540   XFlush(Drv->display);
541 }
542 
543 void
HandleEvent(GRAPHICS_IO_PRIVATE * Drv,XEvent * ev)544 HandleEvent(GRAPHICS_IO_PRIVATE *Drv, XEvent *ev)
545 {
546   switch (ev->type) {
547   case Expose:
548     Redraw (Drv, ev->xexpose.x, ev->xexpose.y,
549       ev->xexpose.width, ev->xexpose.height);
550     break;
551   case GraphicsExpose:
552     Redraw (Drv, ev->xgraphicsexpose.x, ev->xgraphicsexpose.y,
553       ev->xgraphicsexpose.width, ev->xgraphicsexpose.height);
554     break;
555   case KeyPress:
556     handleKeyEvent (Drv, ev, TRUE);
557     break;
558   case KeyRelease:
559     handleKeyEvent (Drv, ev, FALSE);
560     break;
561   case MappingNotify:
562     XRefreshKeyboardMapping (&ev->xmapping);
563     break;
564   case MotionNotify:
565     handleMouseMoved (Drv, ev);
566     break;
567   case ButtonPress:
568     handleMouseDown (Drv, ev, TRUE);
569   break;
570   case ButtonRelease:
571     handleMouseDown (Drv, ev, FALSE);
572   break;
573 #if 0
574   case DestroyNotify:
575     XCloseDisplay (Drv->display);
576     exit (1);
577     break;
578 #endif
579   case NoExpose:
580   default:
581     break;
582   }
583 }
584 
585 void
HandleEvents(IN GRAPHICS_IO_PRIVATE * Drv)586 HandleEvents (
587   IN  GRAPHICS_IO_PRIVATE *Drv
588   )
589 {
590   XEvent ev;
591 
592   while (XPending (Drv->display) != 0) {
593     XNextEvent (Drv->display, &ev);
594     HandleEvent (Drv, &ev);
595   }
596 }
597 
598 unsigned long
X11PixelToColor(IN GRAPHICS_IO_PRIVATE * Drv,IN EFI_UGA_PIXEL pixel)599 X11PixelToColor (
600   IN  GRAPHICS_IO_PRIVATE *Drv,
601   IN  EFI_UGA_PIXEL       pixel
602   )
603 {
604   return ((pixel.Red   >> Drv->r.csize) << Drv->r.shift)
605        | ((pixel.Green >> Drv->g.csize) << Drv->g.shift)
606        | ((pixel.Blue  >> Drv->b.csize) << Drv->b.shift);
607 }
608 
609 EFI_UGA_PIXEL
X11ColorToPixel(IN GRAPHICS_IO_PRIVATE * Drv,IN unsigned long val)610 X11ColorToPixel (
611   IN  GRAPHICS_IO_PRIVATE *Drv,
612   IN  unsigned long       val
613   )
614 {
615   EFI_UGA_PIXEL Pixel;
616 
617   memset (&Pixel, 0, sizeof (EFI_UGA_PIXEL));
618 
619   // Truncation not an issue since X11 and EFI are both using 8 bits per color
620   Pixel.Red =   (val >> Drv->r.shift) << Drv->r.csize;
621   Pixel.Green = (val >> Drv->g.shift) << Drv->g.csize;
622   Pixel.Blue =  (val >> Drv->b.shift) << Drv->b.csize;
623 
624   return Pixel;
625 }
626 
627 
628 EFI_STATUS
X11CheckKey(IN EMU_GRAPHICS_WINDOW_PROTOCOL * GraphicsIo)629 X11CheckKey (
630   IN  EMU_GRAPHICS_WINDOW_PROTOCOL *GraphicsIo
631   )
632 {
633   GRAPHICS_IO_PRIVATE  *Drv;
634 
635   Drv = (GRAPHICS_IO_PRIVATE *)GraphicsIo;
636 
637   HandleEvents (Drv);
638 
639   if (Drv->key_count != 0) {
640     return EFI_SUCCESS;
641   }
642 
643   return EFI_NOT_READY;
644 }
645 
646 EFI_STATUS
X11GetKey(IN EMU_GRAPHICS_WINDOW_PROTOCOL * GraphicsIo,IN EFI_KEY_DATA * KeyData)647 X11GetKey (
648   IN  EMU_GRAPHICS_WINDOW_PROTOCOL  *GraphicsIo,
649   IN  EFI_KEY_DATA                  *KeyData
650   )
651 {
652   EFI_STATUS          EfiStatus;
653   GRAPHICS_IO_PRIVATE *Drv;
654 
655   Drv = (GRAPHICS_IO_PRIVATE *)GraphicsIo;
656 
657   EfiStatus = X11CheckKey (GraphicsIo);
658   if (EFI_ERROR (EfiStatus)) {
659     return EfiStatus;
660   }
661 
662   CopyMem (KeyData, &Drv->keys[Drv->key_rd], sizeof (EFI_KEY_DATA));
663   Drv->key_rd = (Drv->key_rd + 1) % NBR_KEYS;
664   Drv->key_count--;
665 
666   return EFI_SUCCESS;
667 }
668 
669 
670 EFI_STATUS
X11KeySetState(IN EMU_GRAPHICS_WINDOW_PROTOCOL * GraphicsIo,IN EFI_KEY_TOGGLE_STATE * KeyToggleState)671 X11KeySetState (
672   IN EMU_GRAPHICS_WINDOW_PROTOCOL   *GraphicsIo,
673   IN EFI_KEY_TOGGLE_STATE           *KeyToggleState
674   )
675 {
676   GRAPHICS_IO_PRIVATE  *Drv;
677 
678   Drv = (GRAPHICS_IO_PRIVATE *)GraphicsIo;
679 
680   if (*KeyToggleState & EFI_CAPS_LOCK_ACTIVE) {
681     if ((Drv->KeyState.KeyToggleState & EFI_CAPS_LOCK_ACTIVE) == 0) {
682       //
683       // We could create an XKeyEvent and send a XK_Caps_Lock to
684       // the UGA/GOP Window
685       //
686     }
687   }
688 
689   Drv->KeyState.KeyToggleState = *KeyToggleState;
690   return EFI_SUCCESS;
691 }
692 
693 
694 EFI_STATUS
X11RegisterKeyNotify(IN EMU_GRAPHICS_WINDOW_PROTOCOL * GraphicsIo,IN EMU_GRAPHICS_WINDOW_REGISTER_KEY_NOTIFY_CALLBACK MakeCallBack,IN EMU_GRAPHICS_WINDOW_REGISTER_KEY_NOTIFY_CALLBACK BreakCallBack,IN VOID * Context)695 X11RegisterKeyNotify (
696   IN EMU_GRAPHICS_WINDOW_PROTOCOL                        *GraphicsIo,
697   IN EMU_GRAPHICS_WINDOW_REGISTER_KEY_NOTIFY_CALLBACK    MakeCallBack,
698   IN EMU_GRAPHICS_WINDOW_REGISTER_KEY_NOTIFY_CALLBACK    BreakCallBack,
699   IN VOID                                                *Context
700   )
701 {
702   GRAPHICS_IO_PRIVATE  *Drv;
703 
704   Drv = (GRAPHICS_IO_PRIVATE *)GraphicsIo;
705 
706   Drv->MakeRegisterdKeyCallback    = MakeCallBack;
707   Drv->BreakRegisterdKeyCallback   = BreakCallBack;
708   Drv->RegisterdKeyCallbackContext = Context;
709 
710   return EFI_SUCCESS;
711 }
712 
713 
714 EFI_STATUS
X11Blt(IN EMU_GRAPHICS_WINDOW_PROTOCOL * GraphicsIo,IN EFI_UGA_PIXEL * BltBuffer OPTIONAL,IN EFI_UGA_BLT_OPERATION BltOperation,IN EMU_GRAPHICS_WINDOWS__BLT_ARGS * Args)715 X11Blt (
716   IN EMU_GRAPHICS_WINDOW_PROTOCOL             *GraphicsIo,
717   IN  EFI_UGA_PIXEL                           *BltBuffer OPTIONAL,
718   IN  EFI_UGA_BLT_OPERATION                   BltOperation,
719   IN  EMU_GRAPHICS_WINDOWS__BLT_ARGS          *Args
720   )
721 {
722   GRAPHICS_IO_PRIVATE *Private;
723   UINTN             DstY;
724   UINTN             SrcY;
725   UINTN             DstX;
726   UINTN             SrcX;
727   UINTN             Index;
728   EFI_UGA_PIXEL     *Blt;
729   UINT8             *Dst;
730   UINT8             *Src;
731   UINTN             Nbr;
732   unsigned long     Color;
733   XEvent            ev;
734 
735   Private = (GRAPHICS_IO_PRIVATE *)GraphicsIo;
736 
737 
738   //
739   //  Check bounds
740   //
741   if (BltOperation == EfiUgaVideoToBltBuffer
742       || BltOperation == EfiUgaVideoToVideo) {
743     //
744     // Source is Video.
745     //
746     if (Args->SourceY + Args->Height > Private->height) {
747       return EFI_INVALID_PARAMETER;
748     }
749 
750     if (Args->SourceX + Args->Width > Private->width) {
751       return EFI_INVALID_PARAMETER;
752     }
753   }
754 
755   if (BltOperation == EfiUgaBltBufferToVideo
756       || BltOperation == EfiUgaVideoToVideo
757       || BltOperation == EfiUgaVideoFill) {
758     //
759     // Destination is Video
760     //
761     if (Args->DestinationY + Args->Height > Private->height) {
762       return EFI_INVALID_PARAMETER;
763     }
764 
765     if (Args->DestinationX + Args->Width > Private->width) {
766       return EFI_INVALID_PARAMETER;
767     }
768   }
769 
770   switch (BltOperation) {
771   case EfiUgaVideoToBltBuffer:
772     Blt = (EFI_UGA_PIXEL *)((UINT8 *)BltBuffer + (Args->DestinationY * Args->Delta) + Args->DestinationX * sizeof (EFI_UGA_PIXEL));
773     Args->Delta -= Args->Width * sizeof (EFI_UGA_PIXEL);
774     for (SrcY = Args->SourceY; SrcY < (Args->Height + Args->SourceY); SrcY++) {
775       for (SrcX = Args->SourceX; SrcX < (Args->Width + Args->SourceX); SrcX++) {
776         *Blt++ = X11ColorToPixel (Private, XGetPixel (Private->image, SrcX, SrcY));
777       }
778       Blt = (EFI_UGA_PIXEL *) ((UINT8 *) Blt + Args->Delta);
779     }
780     break;
781   case EfiUgaBltBufferToVideo:
782     Blt = (EFI_UGA_PIXEL *)((UINT8 *)BltBuffer + (Args->SourceY * Args->Delta) + Args->SourceX * sizeof (EFI_UGA_PIXEL));
783     Args->Delta -= Args->Width * sizeof (EFI_UGA_PIXEL);
784     for (DstY = Args->DestinationY; DstY < (Args->Height + Args->DestinationY); DstY++) {
785       for (DstX = Args->DestinationX; DstX < (Args->Width + Args->DestinationX); DstX++) {
786         XPutPixel(Private->image, DstX, DstY, X11PixelToColor(Private, *Blt));
787         Blt++;
788       }
789       Blt = (EFI_UGA_PIXEL *) ((UINT8 *) Blt + Args->Delta);
790     }
791     break;
792   case EfiUgaVideoToVideo:
793     Dst = Private->image_data + (Args->DestinationX << Private->pixel_shift)
794           + Args->DestinationY * Private->line_bytes;
795     Src = Private->image_data + (Args->SourceX << Private->pixel_shift)
796           + Args->SourceY * Private->line_bytes;
797     Nbr = Args->Width << Private->pixel_shift;
798     if (Args->DestinationY < Args->SourceY) {
799       for (Index = 0; Index < Args->Height; Index++) {
800         memcpy (Dst, Src, Nbr);
801         Dst += Private->line_bytes;
802         Src += Private->line_bytes;
803       }
804     } else {
805       Dst += (Args->Height - 1) * Private->line_bytes;
806       Src += (Args->Height - 1) * Private->line_bytes;
807       for (Index = 0; Index < Args->Height; Index++) {
808       //
809       // Source and Destination Y may be equal, therefore Dst and Src may
810       // overlap.
811       //
812       memmove (Dst, Src, Nbr);
813       Dst -= Private->line_bytes;
814       Src -= Private->line_bytes;
815       }
816     }
817     break;
818   case EfiUgaVideoFill:
819     Color = X11PixelToColor(Private, *BltBuffer);
820     for (DstY = Args->DestinationY; DstY < (Args->Height + Args->DestinationY); DstY++) {
821       for (DstX = Args->DestinationX; DstX < (Args->Width + Args->DestinationX); DstX++) {
822         XPutPixel(Private->image, DstX, DstY, Color);
823       }
824     }
825     break;
826   default:
827     return EFI_INVALID_PARAMETER;
828   }
829 
830   //
831   //  Refresh screen.
832   //
833   switch (BltOperation) {
834   case EfiUgaVideoToVideo:
835     XCopyArea(
836       Private->display, Private->win, Private->win, Private->gc,
837       Args->SourceX, Args->SourceY, Args->Width, Args->Height,
838       Args->DestinationX, Args->DestinationY
839       );
840 
841     while (1) {
842       XNextEvent (Private->display, &ev);
843       HandleEvent (Private, &ev);
844       if (ev.type == NoExpose || ev.type == GraphicsExpose) {
845         break;
846       }
847     }
848     break;
849   case EfiUgaVideoFill:
850     Color = X11PixelToColor (Private, *BltBuffer);
851     XSetForeground (Private->display, Private->gc, Color);
852     XFillRectangle (
853       Private->display, Private->win, Private->gc,
854       Args->DestinationX, Args->DestinationY, Args->Width, Args->Height
855       );
856     XFlush (Private->display);
857     break;
858   case EfiUgaBltBufferToVideo:
859     Redraw (Private, Args->DestinationX, Args->DestinationY, Args->Width, Args->Height);
860     break;
861   default:
862     break;
863   }
864   return EFI_SUCCESS;
865 }
866 
867 
868 EFI_STATUS
X11CheckPointer(IN EMU_GRAPHICS_WINDOW_PROTOCOL * GraphicsIo)869 X11CheckPointer (
870   IN  EMU_GRAPHICS_WINDOW_PROTOCOL *GraphicsIo
871   )
872 {
873   GRAPHICS_IO_PRIVATE  *Drv;
874 
875   Drv = (GRAPHICS_IO_PRIVATE *)GraphicsIo;
876 
877   HandleEvents (Drv);
878   if (Drv->pointer_state_changed != 0) {
879     return EFI_SUCCESS;
880   }
881 
882   return EFI_NOT_READY;
883 }
884 
885 
886 EFI_STATUS
X11GetPointerState(IN EMU_GRAPHICS_WINDOW_PROTOCOL * GraphicsIo,IN EFI_SIMPLE_POINTER_STATE * State)887 X11GetPointerState (
888   IN  EMU_GRAPHICS_WINDOW_PROTOCOL  *GraphicsIo,
889   IN  EFI_SIMPLE_POINTER_STATE      *State
890   )
891 {
892   EFI_STATUS          EfiStatus;
893   GRAPHICS_IO_PRIVATE *Drv;
894 
895   Drv = (GRAPHICS_IO_PRIVATE *)GraphicsIo;
896 
897   EfiStatus = X11CheckPointer (GraphicsIo);
898   if (EfiStatus != EFI_SUCCESS) {
899     return EfiStatus;
900   }
901 
902   memcpy (State, &Drv->pointer_state, sizeof (EFI_SIMPLE_POINTER_STATE));
903 
904   Drv->pointer_state.RelativeMovementX = 0;
905   Drv->pointer_state.RelativeMovementY = 0;
906   Drv->pointer_state.RelativeMovementZ = 0;
907   Drv->pointer_state_changed = 0;
908   return EFI_SUCCESS;
909 }
910 
911 
912 
913 EFI_STATUS
X11GraphicsWindowOpen(IN EMU_IO_THUNK_PROTOCOL * This)914 X11GraphicsWindowOpen (
915   IN  EMU_IO_THUNK_PROTOCOL   *This
916   )
917 {
918   GRAPHICS_IO_PRIVATE *Drv;
919   unsigned int        border_width = 0;
920   char                *display_name = NULL;
921 
922   Drv = (GRAPHICS_IO_PRIVATE *)calloc (1, sizeof (GRAPHICS_IO_PRIVATE));
923   if (Drv == NULL) {
924     return EFI_OUT_OF_RESOURCES;
925   }
926 
927   Drv->GraphicsIo.Size                = GasketX11Size;
928   Drv->GraphicsIo.CheckKey            = GasketX11CheckKey;
929   Drv->GraphicsIo.GetKey              = GasketX11GetKey;
930   Drv->GraphicsIo.KeySetState         = GasketX11KeySetState;
931   Drv->GraphicsIo.RegisterKeyNotify   = GasketX11RegisterKeyNotify;
932   Drv->GraphicsIo.Blt                 = GasketX11Blt;
933   Drv->GraphicsIo.CheckPointer        = GasketX11CheckPointer;
934   Drv->GraphicsIo.GetPointerState     = GasketX11GetPointerState;
935 
936 
937   Drv->key_count = 0;
938   Drv->key_rd = 0;
939   Drv->key_wr = 0;
940   Drv->KeyState.KeyShiftState      = EFI_SHIFT_STATE_VALID;
941   Drv->KeyState.KeyToggleState     = EFI_TOGGLE_STATE_VALID;
942   Drv->MakeRegisterdKeyCallback    = NULL;
943   Drv->BreakRegisterdKeyCallback   = NULL;
944   Drv->RegisterdKeyCallbackContext = NULL;
945 
946 
947   Drv->display = XOpenDisplay (display_name);
948   if (Drv->display == NULL) {
949     fprintf (stderr, "uga: cannot connect to X server %s\n", XDisplayName (display_name));
950     free (Drv);
951     return EFI_DEVICE_ERROR;
952   }
953   Drv->screen = DefaultScreen (Drv->display);
954   Drv->visual = DefaultVisual (Drv->display, Drv->screen);
955   Drv->win = XCreateSimpleWindow (
956                 Drv->display, RootWindow (Drv->display, Drv->screen),
957                 0, 0, 4, 4, border_width,
958                 WhitePixel (Drv->display, Drv->screen),
959                 BlackPixel (Drv->display, Drv->screen)
960                 );
961 
962   Drv->depth = DefaultDepth (Drv->display, Drv->screen);
963   XDefineCursor (Drv->display, Drv->win, XCreateFontCursor (Drv->display, XC_pirate));
964 
965   Drv->Title = malloc (StrSize (This->ConfigString));
966   UnicodeStrToAsciiStr (This->ConfigString, Drv->Title);
967   XStoreName (Drv->display, Drv->win, Drv->Title);
968 
969 //  XAutoRepeatOff (Drv->display);
970   XSelectInput (
971     Drv->display, Drv->win,
972     ExposureMask | KeyPressMask | KeyReleaseMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask
973     );
974   Drv->gc = DefaultGC (Drv->display, Drv->screen);
975 
976   This->Private   = (VOID *)Drv;
977   This->Interface = (VOID *)Drv;
978   return EFI_SUCCESS;
979 }
980 
981 
982 EFI_STATUS
X11GraphicsWindowClose(IN EMU_IO_THUNK_PROTOCOL * This)983 X11GraphicsWindowClose (
984   IN  EMU_IO_THUNK_PROTOCOL   *This
985   )
986 {
987   GRAPHICS_IO_PRIVATE *Drv;
988 
989   Drv = (GRAPHICS_IO_PRIVATE *)This->Private;
990 
991   if (Drv == NULL) {
992     return EFI_SUCCESS;
993   }
994 
995   if (Drv->image != NULL) {
996     XDestroyImage(Drv->image);
997 
998     if (Drv->use_shm) {
999       shmdt (Drv->image_data);
1000     }
1001 
1002     Drv->image_data = NULL;
1003     Drv->image = NULL;
1004   }
1005   XDestroyWindow (Drv->display, Drv->win);
1006   XCloseDisplay (Drv->display);
1007 
1008 #ifdef __APPLE__
1009   // Free up the shared memory
1010   shmctl (Drv->xshm_info.shmid, IPC_RMID, NULL);
1011 #endif
1012 
1013   free (Drv);
1014   return EFI_SUCCESS;
1015 }
1016 
1017 
1018 EMU_IO_THUNK_PROTOCOL gX11ThunkIo = {
1019   &gEmuGraphicsWindowProtocolGuid,
1020   NULL,
1021   NULL,
1022   0,
1023   GasketX11GraphicsWindowOpen,
1024   GasketX11GraphicsWindowClose,
1025   NULL
1026 };
1027 
1028 
1029