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