use nanosleep instead of usleep, ugaX11 calls msSleep instead of usleep
[people/mcb30/edk2.git] / edk2 / EdkUnixPkg / Sec / UgaX11.c
1 #include "Uefi/UefiSpec.h"
2 #include "Protocol/UnixThunk.h"
3 #include "Protocol/SimpleTextIn.h"
4 #include "Protocol/UgaDraw.h"
5 #include "Protocol/UnixUgaIo.h"
6 #include <X11/Xlib.h>
7 #include <X11/Xutil.h>
8 #include <X11/Xos.h>
9 #include <X11/extensions/XShm.h>
10 #include <X11/keysym.h>
11 #include <sys/ipc.h>
12 #include <sys/shm.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15
16 extern void msSleep (unsigned long Milliseconds);
17
18 /* XQueryPointer  */
19
20 struct uga_drv_shift_mask
21 {
22   unsigned char shift;
23   unsigned char size;
24   unsigned char csize;
25 };
26
27 #define NBR_KEYS 32
28 typedef struct
29 {
30   EFI_UNIX_UGA_IO_PROTOCOL UgaIo;
31
32   Display *display;
33   int screen;                   /* values for window_size in main */
34   Window win;
35   GC gc;
36   Visual *visual;
37
38   int depth;
39   unsigned int width;
40   unsigned int height;
41   unsigned int line_bytes;
42   unsigned int pixel_shift;
43   unsigned char *image_data;
44
45   struct uga_drv_shift_mask r, g, b;
46
47   int use_shm;
48   XShmSegmentInfo xshm_info;
49   XImage *image;
50
51   unsigned int key_rd;
52   unsigned int key_wr;
53   unsigned int key_count;
54   EFI_INPUT_KEY keys[NBR_KEYS];
55 } UGA_IO_PRIVATE;
56
57 static void
58 HandleEvents(UGA_IO_PRIVATE *drv);
59
60 static void
61 fill_shift_mask (struct uga_drv_shift_mask *sm, unsigned long mask)
62 {
63   sm->shift = 0;
64   sm->size = 0;
65   while ((mask & 1) == 0)
66     {
67       mask >>= 1;
68       sm->shift++;
69     }
70   while (mask & 1)
71     {
72       sm->size++;
73       mask >>= 1;
74     }
75   sm->csize = 8 - sm->size;
76 }
77
78 static int
79 TryCreateShmImage(UGA_IO_PRIVATE *drv)
80 {
81   drv->image = XShmCreateImage (drv->display, drv->visual,
82                                 drv->depth, ZPixmap, NULL, &drv->xshm_info,
83                                 drv->width, drv->height);
84   if (drv->image == NULL)
85     return 0;
86
87   switch (drv->image->bitmap_unit) {
88   case 32:
89     drv->pixel_shift = 2;
90     break;
91   case 16:
92     drv->pixel_shift = 1;
93     break;
94   case 8:
95     drv->pixel_shift = 0;
96     break;
97   }
98
99   drv->xshm_info.shmid = shmget
100         (IPC_PRIVATE, drv->image->bytes_per_line * drv->image->height,
101          IPC_CREAT | 0777);
102   if (drv->xshm_info.shmid < 0)
103     {
104       XDestroyImage(drv->image);
105       return 0;
106     }
107       
108   drv->image_data = shmat (drv->xshm_info.shmid, NULL, 0);
109   if(!drv->image_data)
110     {
111       shmctl (drv->xshm_info.shmid, IPC_RMID, NULL);
112       XDestroyImage(drv->image);
113       return 0;
114     }
115   /* Can this fail ?  */
116   shmctl (drv->xshm_info.shmid, IPC_RMID, NULL);
117
118   drv->xshm_info.shmaddr = (char*)drv->image_data;
119   drv->image->data = (char*)drv->image_data;
120           
121   if (!XShmAttach (drv->display, &drv->xshm_info))
122     {
123       shmdt (drv->image_data);
124       XDestroyImage(drv->image);
125       return 0;
126     }
127   return 1;
128 }
129
130 static
131 EFI_STATUS
132 UgaClose (EFI_UNIX_UGA_IO_PROTOCOL *UgaIo)
133 {
134   UGA_IO_PRIVATE *drv = (UGA_IO_PRIVATE *)UgaIo;
135
136   if (drv == NULL)
137     return EFI_SUCCESS;
138   if (drv->image != NULL)
139     {
140       XDestroyImage(drv->image);
141
142       if (drv->use_shm)
143         shmdt (drv->image_data);
144
145       drv->image_data = NULL;
146       drv->image = NULL;
147     }
148   XDestroyWindow(drv->display, drv->win);
149   XCloseDisplay(drv->display);
150   free(drv);
151   return EFI_SUCCESS;
152 }
153
154 static
155 EFI_STATUS
156 UgaSize(EFI_UNIX_UGA_IO_PROTOCOL *UgaIo, UINT32 Width, UINT32 Height)
157 {
158   UGA_IO_PRIVATE *drv = (UGA_IO_PRIVATE *)UgaIo;
159   XSizeHints size_hints;
160
161   /* Destroy current buffer if created.  */
162   if (drv->image != NULL)
163     {
164       XDestroyImage(drv->image);
165
166       if (drv->use_shm)
167         shmdt (drv->image_data);
168
169       drv->image_data = NULL;
170       drv->image = NULL;
171     }
172
173   drv->width = Width;
174   drv->height = Height;
175   XResizeWindow (drv->display, drv->win, Width, Height);
176
177   /* Allocate image.  */
178   if (XShmQueryExtension(drv->display) && TryCreateShmImage(drv))
179     {
180       drv->use_shm = 1;
181     }   
182   else  
183     {
184       drv->use_shm = 0;
185       if (drv->depth > 16)
186         drv->pixel_shift = 2;
187       else if (drv->depth > 8)
188         drv->pixel_shift = 1;
189       else
190         drv->pixel_shift = 0;
191       
192       drv->image_data = malloc((drv->width * drv->height) << drv->pixel_shift);
193       drv->image = XCreateImage (drv->display, drv->visual, drv->depth,
194                                  ZPixmap, 0, (char *)drv->image_data,
195                                  drv->width, drv->height,
196                                  8 << drv->pixel_shift, 0);
197     }
198   drv->line_bytes = drv->image->bytes_per_line;
199   fill_shift_mask (&drv->r, drv->image->red_mask);
200   fill_shift_mask (&drv->g, drv->image->green_mask);
201   fill_shift_mask (&drv->b, drv->image->blue_mask);
202
203   /* Set WM hints.  */
204   size_hints.flags = PSize | PMinSize | PMaxSize;
205   size_hints.min_width = size_hints.max_width = size_hints.base_width = Width;
206   size_hints.min_height = size_hints.max_height = size_hints.base_height = Height;
207   XSetWMNormalHints (drv->display, drv->win, &size_hints);
208
209   XMapWindow (drv->display, drv->win);
210   HandleEvents(drv);
211   return EFI_SUCCESS;
212 }
213
214 static void
215 handleKeyEvent(UGA_IO_PRIVATE *drv, XEvent *ev)
216 {
217   KeySym keysym;
218   char str[4];
219   EFI_INPUT_KEY Key;
220   int res;
221
222   if (drv->key_count == NBR_KEYS)
223     return;
224
225   res = XLookupString(&ev->xkey, str, sizeof(str), &keysym, NULL);
226   Key.ScanCode = 0;\r
227   Key.UnicodeChar = 0;
228   switch (keysym) {\r
229   case XK_Home:       Key.ScanCode = SCAN_HOME;       break;\r
230   case XK_End:        Key.ScanCode = SCAN_END;        break;\r
231   case XK_Left:       Key.ScanCode = SCAN_LEFT;       break;\r
232   case XK_Right:      Key.ScanCode = SCAN_RIGHT;      break;\r
233   case XK_Up:         Key.ScanCode = SCAN_UP;         break;\r
234   case XK_Down:       Key.ScanCode = SCAN_DOWN;       break;\r
235   case XK_Delete:     Key.ScanCode = SCAN_DELETE;     break;\r
236   case XK_Insert:     Key.ScanCode = SCAN_INSERT;     break;\r
237   case XK_Page_Up:    Key.ScanCode = SCAN_PAGE_UP;    break;\r
238   case XK_Page_Down:  Key.ScanCode = SCAN_PAGE_DOWN;  break;\r
239   case XK_Escape:     Key.ScanCode = SCAN_ESC;        break;\r
240 \r
241   case XK_F1:   Key.ScanCode = SCAN_F1;   break;\r
242   case XK_F2:   Key.ScanCode = SCAN_F2;   break;\r
243   case XK_F3:   Key.ScanCode = SCAN_F3;   break;\r
244   case XK_F4:   Key.ScanCode = SCAN_F4;   break;\r
245   case XK_F5:   Key.ScanCode = SCAN_F5;   break;\r
246   case XK_F6:   Key.ScanCode = SCAN_F6;   break;\r
247   case XK_F7:   Key.ScanCode = SCAN_F7;   break;\r
248   case XK_F8:   Key.ScanCode = SCAN_F8;   break;\r
249   case XK_F9:   Key.ScanCode = SCAN_F9;   break;\r
250
251   default:
252     if (res == 1) {
253       Key.UnicodeChar = str[0];
254     } else {
255       return;
256     }
257   }\r
258 \r
259   drv->keys[drv->key_wr] = Key;
260   drv->key_wr = (drv->key_wr + 1) % NBR_KEYS;
261   drv->key_count++;
262 }
263
264 static void
265 Redraw(UGA_IO_PRIVATE *drv, UINTN X, UINTN Y, UINTN Width, UINTN Height)
266 {
267   if (drv->use_shm)
268     XShmPutImage (drv->display, drv->win, drv->gc, drv->image,
269                   X, Y, X, Y, Width, Height, False);
270   else
271     XPutImage (drv->display, drv->win, drv->gc, drv->image,
272                   X, Y, X, Y, Width, Height);
273 }
274
275 static void
276 HandleEvent(UGA_IO_PRIVATE *drv, XEvent *ev)
277 {
278   switch (ev->type)
279     {
280     case Expose:
281       Redraw(drv, ev->xexpose.x, ev->xexpose.y,
282              ev->xexpose.width, ev->xexpose.height);
283       break;
284     case GraphicsExpose:
285       Redraw(drv, ev->xgraphicsexpose.x, ev->xgraphicsexpose.y,
286              ev->xgraphicsexpose.width, ev->xgraphicsexpose.height);
287       break;
288     case KeyPress:
289       handleKeyEvent(drv, ev);
290       break;
291     case MappingNotify:
292       XRefreshKeyboardMapping(&ev->xmapping);
293       break;
294 #if 0
295     case DestroyNotify:
296       XCloseDisplay (drv->display);
297       exit (1);
298       break;
299 #endif
300     case NoExpose:
301     default:
302       break;
303     }
304 }
305
306 static void
307 HandleEvents(UGA_IO_PRIVATE *drv)
308 {
309   while (XPending(drv->display) != 0)
310     {
311       XEvent ev;
312           
313       XNextEvent (drv->display, &ev);
314       HandleEvent(drv, &ev);
315     }
316 }
317
318 static
319 unsigned long
320 UgaPixelToColor (UGA_IO_PRIVATE *drv, EFI_UGA_PIXEL pixel)
321 {
322   return ((pixel.Red >> drv->r.csize) << drv->r.shift)
323     | ((pixel.Green >> drv->g.csize) << drv->g.shift)
324     | ((pixel.Blue >> drv->b.csize) << drv->b.shift);
325 }
326
327 static
328 EFI_UGA_PIXEL
329 UgaColorToPixel (UGA_IO_PRIVATE *drv, unsigned long val)
330 {
331   EFI_UGA_PIXEL res;
332
333   memset (&res, 0, sizeof (EFI_UGA_PIXEL));
334   /* FIXME: should round instead of truncate.  */
335   res.Red = (val >> drv->r.shift) << drv->r.csize;
336   res.Green = (val >> drv->g.shift) << drv->g.csize;
337   res.Blue = (val >> drv->b.shift) << drv->b.csize;
338
339   return res;
340 }
341
342 static
343 EFI_STATUS
344 UgaCheckKey(EFI_UNIX_UGA_IO_PROTOCOL *UgaIo)
345 {
346   UGA_IO_PRIVATE *drv = (UGA_IO_PRIVATE *)UgaIo;
347   HandleEvents(drv);
348   if (drv->key_count != 0)
349     return EFI_SUCCESS;
350   else {
351     /* EFI is certainly polling.  Be CPU-friendly.  */
352     msSleep (20);
353     return EFI_NOT_READY;
354   }
355 }
356
357 static
358 EFI_STATUS
359 UgaGetKey(EFI_UNIX_UGA_IO_PROTOCOL *UgaIo, EFI_INPUT_KEY *key)
360 {
361   UGA_IO_PRIVATE *drv = (UGA_IO_PRIVATE *)UgaIo;
362   EFI_STATUS status;
363
364   status = UgaCheckKey(UgaIo);
365   if (status != EFI_SUCCESS)
366     return status;
367
368   *key = drv->keys[drv->key_rd];
369   drv->key_rd = (drv->key_rd + 1) % NBR_KEYS;
370   drv->key_count--;
371   return EFI_SUCCESS;
372 }
373
374 EFI_STATUS\r
375 UgaBlt(EFI_UNIX_UGA_IO_PROTOCOL *UgaIo,
376        IN  EFI_UGA_PIXEL                           *BltBuffer OPTIONAL,\r
377        IN  EFI_UGA_BLT_OPERATION                   BltOperation,\r
378        IN  UINTN                                   SourceX,\r
379        IN  UINTN                                   SourceY,\r
380        IN  UINTN                                   DestinationX,\r
381        IN  UINTN                                   DestinationY,\r
382        IN  UINTN                                   Width,\r
383        IN  UINTN                                   Height,\r
384        IN  UINTN                                   Delta OPTIONAL\r
385   )\r
386 {
387   UGA_IO_PRIVATE *Private = (UGA_IO_PRIVATE *)UgaIo;
388   UINTN             DstY;\r
389   UINTN             SrcY;\r
390   UINTN             DstX;
391   UINTN             SrcX;
392   UINTN             Index;
393   EFI_UGA_PIXEL     *Blt;\r
394   UINT8             *Dst;
395   UINT8             *Src;
396   UINTN             Nbr;
397   unsigned long     Color;
398
399   //
400   //  Check bounds
401   //
402   if (BltOperation == EfiUgaVideoToBltBuffer
403       || BltOperation == EfiUgaVideoToVideo) {\r
404     //\r
405     // Source is Video.
406     //\r
407     if (SourceY + Height > Private->height) {\r
408       return EFI_INVALID_PARAMETER;\r
409     }\r
410 \r
411     if (SourceX + Width > Private->width) {\r
412       return EFI_INVALID_PARAMETER;\r
413     }\r
414   }
415
416   if (BltOperation == EfiUgaBltBufferToVideo
417       || BltOperation == EfiUgaVideoToVideo
418       || BltOperation == EfiUgaVideoFill) {\r
419     //\r
420     // Destination is Video\r
421     //\r
422     if (DestinationY + Height > Private->height) {\r
423       return EFI_INVALID_PARAMETER;\r
424     }\r
425 \r
426     if (DestinationX + Width > Private->width) {\r
427       return EFI_INVALID_PARAMETER;\r
428     }\r
429   }
430
431   switch (BltOperation) {\r
432   case EfiUgaVideoToBltBuffer:
433     Blt = BltBuffer;
434     Delta -= Width * sizeof (EFI_UGA_PIXEL);
435     for (SrcY = SourceY; SrcY < (Height + SourceY); SrcY++) {\r
436       for (SrcX = SourceX; SrcX < (Width + SourceX); SrcX++) {
437         *Blt++ = UgaColorToPixel(Private,
438                                  XGetPixel(Private->image, SrcX, SrcY));
439       }
440       Blt = (EFI_UGA_PIXEL *) ((UINT8 *) Blt + Delta);
441     }\r
442     break;
443   case EfiUgaBltBufferToVideo:\r
444     Blt = BltBuffer;
445     Delta -= Width * sizeof (EFI_UGA_PIXEL);
446     for (DstY = DestinationY; DstY < (Height + DestinationY); DstY++) {\r
447       for (DstX = DestinationX; DstX < (Width + DestinationX); DstX++) {\r
448         XPutPixel(Private->image, DstX, DstY, UgaPixelToColor(Private, *Blt));
449         Blt++;
450       }
451       Blt = (EFI_UGA_PIXEL *) ((UINT8 *) Blt + Delta);
452     }
453     break;
454   case EfiUgaVideoToVideo:\r
455     Dst = Private->image_data + (DestinationX << Private->pixel_shift)
456       + DestinationY * Private->line_bytes;
457     Src = Private->image_data + (SourceX << Private->pixel_shift)
458       + SourceY * Private->line_bytes;
459     Nbr = Width << Private->pixel_shift;
460     if (DestinationY < SourceY) {\r
461       for (Index = 0; Index < Height; Index++) {
462         memcpy (Dst, Src, Nbr);
463         Dst += Private->line_bytes;
464         Src += Private->line_bytes;
465       }
466     }
467     else {
468       Dst += (Height - 1) * Private->line_bytes;
469       Src += (Height - 1) * Private->line_bytes;
470       for (Index = 0; Index < Height; Index++) {
471         //
472         // Source and Destination Y may be equal, therefore Dst and Src may
473         // overlap.
474         //
475         memmove (Dst, Src, Nbr);
476         Dst -= Private->line_bytes;
477         Src -= Private->line_bytes;
478       }
479     }
480     break;
481   case EfiUgaVideoFill:
482     Color = UgaPixelToColor(Private, *BltBuffer);
483     for (DstY = DestinationY; DstY < (Height + DestinationY); DstY++) {\r
484       for (DstX = DestinationX; DstX < (Width + DestinationX); DstX++) {
485         XPutPixel(Private->image, DstX, DstY, Color);
486       }
487     }\r
488     break;
489   default:
490       return EFI_INVALID_PARAMETER;\r
491   }
492
493   //
494   //  Refresh screen.
495   //
496   switch (BltOperation) {\r
497   case EfiUgaVideoToVideo:\r
498     XCopyArea(Private->display, Private->win, Private->win, Private->gc,
499               SourceX, SourceY, Width, Height, DestinationX, DestinationY);
500     while (1) {
501       XEvent ev;
502           
503       XNextEvent (Private->display, &ev);
504       HandleEvent(Private, &ev);
505       if (ev.type == NoExpose || ev.type == GraphicsExpose)
506         break;
507     }
508     break;
509   case EfiUgaVideoFill:
510     Color = UgaPixelToColor(Private, *BltBuffer);
511     XSetForeground(Private->display, Private->gc, Color);
512     XFillRectangle(Private->display, Private->win, Private->gc,
513                    DestinationX, DestinationY, Width, Height);
514     break;
515   case EfiUgaBltBufferToVideo:\r
516     Redraw(Private, DestinationX, DestinationY, Width, Height);
517     break;
518   default:
519     break;
520   }
521   return EFI_SUCCESS;
522 }
523
524 EFI_STATUS
525 UgaCreate (EFI_UNIX_UGA_IO_PROTOCOL **Uga, CONST CHAR16 *Title)
526 {
527   UGA_IO_PRIVATE *drv;
528   unsigned int border_width = 0;
529   char *display_name = NULL;
530   int title_len;
531
532   drv = (UGA_IO_PRIVATE *)
533     calloc (1, sizeof (UGA_IO_PRIVATE));
534   if (drv == NULL)
535     return EFI_OUT_OF_RESOURCES;
536
537   drv->UgaIo.UgaClose = UgaClose;
538   drv->UgaIo.UgaSize = UgaSize;
539   drv->UgaIo.UgaCheckKey = UgaCheckKey;
540   drv->UgaIo.UgaGetKey = UgaGetKey;
541   drv->UgaIo.UgaBlt = UgaBlt;
542
543   drv->key_count = 0;
544   drv->key_rd = 0;
545   drv->key_wr = 0;
546   drv->display = XOpenDisplay (display_name);
547   if (drv->display == NULL)
548     {
549       fprintf (stderr, "uga: cannot connect to X server %s\n",
550                XDisplayName (display_name));
551       free (drv);
552       return EFI_DEVICE_ERROR;
553     }
554   drv->screen = DefaultScreen (drv->display);
555   drv->visual = DefaultVisual (drv->display, drv->screen);
556   drv->win = XCreateSimpleWindow
557         (drv->display, RootWindow (drv->display, drv->screen),
558          0, 0, 4, 4, border_width,
559          BlackPixel (drv->display, drv->screen),
560          WhitePixel (drv->display, drv->screen));
561
562   drv->depth = DefaultDepth (drv->display, drv->screen);
563
564   /* Compute title len and convert to Ascii.  */
565   for (title_len = 0; Title[title_len] != 0; title_len++)
566     ;
567   {
568     char title[title_len + 1];
569     int i;
570     for (i = 0; i < title_len; i++)
571       title[i] = Title[i];
572     title[i] = 0;
573     
574     XStoreName (drv->display, drv->win, title);
575   }
576
577   XSelectInput (drv->display, drv->win,
578                 ExposureMask | KeyPressMask);
579   drv->gc = DefaultGC (drv->display, drv->screen);
580
581   *Uga = (EFI_UNIX_UGA_IO_PROTOCOL *)drv;
582   return EFI_SUCCESS;
583 }