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