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