b5719cfd596adeac8a635427bc5db4513a712ec5
[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 = (char*)drv->image_data;
117 drv->image->data = (char*)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, (char *)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 memset (&res, 0, sizeof (EFI_UGA_PIXEL));
332 /* FIXME: should round instead of truncate. */
333 res.Red = (val >> drv->r.shift) << drv->r.csize;
334 res.Green = (val >> drv->g.shift) << drv->g.csize;
335 res.Blue = (val >> drv->b.shift) << drv->b.csize;
336
337 return res;
338 }
339
340 static
341 EFI_STATUS
342 UgaCheckKey(EFI_UNIX_UGA_IO_PROTOCOL *UgaIo)
343 {
344 UGA_IO_PRIVATE *drv = (UGA_IO_PRIVATE *)UgaIo;
345 HandleEvents(drv);
346 if (drv->key_count != 0)
347 return EFI_SUCCESS;
348 else {
349 /* EFI is certainly polling. Be CPU-friendly. */
350 usleep (50000);
351 return EFI_NOT_READY;
352 }
353 }
354
355 static
356 EFI_STATUS
357 UgaGetKey(EFI_UNIX_UGA_IO_PROTOCOL *UgaIo, EFI_INPUT_KEY *key)
358 {
359 UGA_IO_PRIVATE *drv = (UGA_IO_PRIVATE *)UgaIo;
360 EFI_STATUS status;
361
362 status = UgaCheckKey(UgaIo);
363 if (status != EFI_SUCCESS)
364 return status;
365
366 *key = drv->keys[drv->key_rd];
367 drv->key_rd = (drv->key_rd + 1) % NBR_KEYS;
368 drv->key_count--;
369 return EFI_SUCCESS;
370 }
371
372 EFI_STATUS
373 UgaBlt(EFI_UNIX_UGA_IO_PROTOCOL *UgaIo,
374 IN EFI_UGA_PIXEL *BltBuffer OPTIONAL,
375 IN EFI_UGA_BLT_OPERATION BltOperation,
376 IN UINTN SourceX,
377 IN UINTN SourceY,
378 IN UINTN DestinationX,
379 IN UINTN DestinationY,
380 IN UINTN Width,
381 IN UINTN Height,
382 IN UINTN Delta OPTIONAL
383 )
384 {
385 UGA_IO_PRIVATE *Private = (UGA_IO_PRIVATE *)UgaIo;
386 UINTN DstY;
387 UINTN SrcY;
388 UINTN DstX;
389 UINTN SrcX;
390 UINTN Index;
391 EFI_UGA_PIXEL *Blt;
392 UINT8 *Dst;
393 UINT8 *Src;
394 UINTN Nbr;
395 unsigned long Color;
396
397 //
398 // Check bounds
399 //
400 if (BltOperation == EfiUgaVideoToBltBuffer
401 || BltOperation == EfiUgaVideoToVideo) {
402 //
403 // Source is Video.
404 //
405 if (SourceY + Height > Private->height) {
406 return EFI_INVALID_PARAMETER;
407 }
408
409 if (SourceX + Width > Private->width) {
410 return EFI_INVALID_PARAMETER;
411 }
412 }
413
414 if (BltOperation == EfiUgaBltBufferToVideo
415 || BltOperation == EfiUgaVideoToVideo
416 || BltOperation == EfiUgaVideoFill) {
417 //
418 // Destination is Video
419 //
420 if (DestinationY + Height > Private->height) {
421 return EFI_INVALID_PARAMETER;
422 }
423
424 if (DestinationX + Width > Private->width) {
425 return EFI_INVALID_PARAMETER;
426 }
427 }
428
429 switch (BltOperation) {
430 case EfiUgaVideoToBltBuffer:
431 Blt = BltBuffer;
432 Delta -= Width * sizeof (EFI_UGA_PIXEL);
433 for (SrcY = SourceY; SrcY < (Height + SourceY); SrcY++) {
434 for (SrcX = SourceX; SrcX < (Width + SourceX); SrcX++) {
435 *Blt++ = UgaColorToPixel(Private,
436 XGetPixel(Private->image, SrcX, SrcY));
437 }
438 Blt = (EFI_UGA_PIXEL *) ((UINT8 *) Blt + Delta);
439 }
440 break;
441 case EfiUgaBltBufferToVideo:
442 Blt = BltBuffer;
443 Delta -= Width * sizeof (EFI_UGA_PIXEL);
444 for (DstY = DestinationY; DstY < (Height + DestinationY); DstY++) {
445 for (DstX = DestinationX; DstX < (Width + DestinationX); DstX++) {
446 XPutPixel(Private->image, DstX, DstY, UgaPixelToColor(Private, *Blt));
447 Blt++;
448 }
449 Blt = (EFI_UGA_PIXEL *) ((UINT8 *) Blt + Delta);
450 }
451 break;
452 case EfiUgaVideoToVideo:
453 Dst = Private->image_data + (DestinationX << Private->pixel_shift)
454 + DestinationY * Private->line_bytes;
455 Src = Private->image_data + (SourceX << Private->pixel_shift)
456 + SourceY * Private->line_bytes;
457 Nbr = Width << Private->pixel_shift;
458 if (DestinationY < SourceY) {
459 for (Index = 0; Index < Height; Index++) {
460 memcpy (Dst, Src, Nbr);
461 Dst += Private->line_bytes;
462 Src += Private->line_bytes;
463 }
464 }
465 else {
466 Dst += (Height - 1) * Private->line_bytes;
467 Src += (Height - 1) * Private->line_bytes;
468 for (Index = 0; Index < Height; Index++) {
469 //
470 // Source and Destination Y may be equal, therefore Dst and Src may
471 // overlap.
472 //
473 memmove (Dst, Src, Nbr);
474 Dst -= Private->line_bytes;
475 Src -= Private->line_bytes;
476 }
477 }
478 break;
479 case EfiUgaVideoFill:
480 Color = UgaPixelToColor(Private, *BltBuffer);
481 for (DstY = DestinationY; DstY < (Height + DestinationY); DstY++) {
482 for (DstX = DestinationX; DstX < (Width + DestinationX); DstX++) {
483 XPutPixel(Private->image, DstX, DstY, Color);
484 }
485 }
486 break;
487 default:
488 return EFI_INVALID_PARAMETER;
489 }
490
491 //
492 // Refresh screen.
493 //
494 switch (BltOperation) {
495 case EfiUgaVideoToVideo:
496 XCopyArea(Private->display, Private->win, Private->win, Private->gc,
497 SourceX, SourceY, Width, Height, DestinationX, DestinationY);
498 while (1) {
499 XEvent ev;
500
501 XNextEvent (Private->display, &ev);
502 HandleEvent(Private, &ev);
503 if (ev.type == NoExpose || ev.type == GraphicsExpose)
504 break;
505 }
506 break;
507 case EfiUgaVideoFill:
508 Color = UgaPixelToColor(Private, *BltBuffer);
509 XSetForeground(Private->display, Private->gc, Color);
510 XFillRectangle(Private->display, Private->win, Private->gc,
511 DestinationX, DestinationY, Width, Height);
512 break;
513 case EfiUgaBltBufferToVideo:
514 Redraw(Private, DestinationX, DestinationY, Width, Height);
515 break;
516 default:
517 break;
518 }
519 return EFI_SUCCESS;
520 }
521
522 EFI_STATUS
523 UgaCreate (EFI_UNIX_UGA_IO_PROTOCOL **Uga, CONST CHAR16 *Title)
524 {
525 UGA_IO_PRIVATE *drv;
526 unsigned int border_width = 0;
527 char *display_name = NULL;
528 int title_len;
529
530 drv = (UGA_IO_PRIVATE *)
531 calloc (1, sizeof (UGA_IO_PRIVATE));
532 if (drv == NULL)
533 return EFI_OUT_OF_RESOURCES;
534
535 drv->UgaIo.UgaClose = UgaClose;
536 drv->UgaIo.UgaSize = UgaSize;
537 drv->UgaIo.UgaCheckKey = UgaCheckKey;
538 drv->UgaIo.UgaGetKey = UgaGetKey;
539 drv->UgaIo.UgaBlt = UgaBlt;
540
541 drv->key_count = 0;
542 drv->key_rd = 0;
543 drv->key_wr = 0;
544 drv->display = XOpenDisplay (display_name);
545 if (drv->display == NULL)
546 {
547 fprintf (stderr, "uga: cannot connect to X server %s\n",
548 XDisplayName (display_name));
549 free (drv);
550 return EFI_DEVICE_ERROR;
551 }
552 drv->screen = DefaultScreen (drv->display);
553 drv->visual = DefaultVisual (drv->display, drv->screen);
554 drv->win = XCreateSimpleWindow
555 (drv->display, RootWindow (drv->display, drv->screen),
556 0, 0, 4, 4, border_width,
557 BlackPixel (drv->display, drv->screen),
558 WhitePixel (drv->display, drv->screen));
559
560 drv->depth = DefaultDepth (drv->display, drv->screen);
561
562 /* Compute title len and convert to Ascii. */
563 for (title_len = 0; Title[title_len] != 0; title_len++)
564 ;
565 {
566 char title[title_len + 1];
567 int i;
568 for (i = 0; i < title_len; i++)
569 title[i] = Title[i];
570 title[i] = 0;
571
572 XStoreName (drv->display, drv->win, title);
573 }
574
575 XSelectInput (drv->display, drv->win,
576 ExposureMask | KeyPressMask);
577 drv->gc = DefaultGC (drv->display, drv->screen);
578
579 *Uga = (EFI_UNIX_UGA_IO_PROTOCOL *)drv;
580 return EFI_SUCCESS;
581 }