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