]> git.proxmox.com Git - mirror_edk2.git/blob - UnixPkg/Sec/UgaX11.c
Complete coding to support X64 EFI ABI in UnixPkg. Code is not currently hooked in...
[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(
404 IN EFI_UNIX_UGA_IO_PROTOCOL *UgaIo,
405 IN EFI_UGA_PIXEL *BltBuffer OPTIONAL,
406 IN EFI_UGA_BLT_OPERATION BltOperation,
407 IN UGA_BLT_ARGS *Args
408 )
409 {
410 UGA_IO_PRIVATE *Private = (UGA_IO_PRIVATE *)UgaIo;
411 UINTN DstY;
412 UINTN SrcY;
413 UINTN DstX;
414 UINTN SrcX;
415 UINTN Index;
416 EFI_UGA_PIXEL *Blt;
417 UINT8 *Dst;
418 UINT8 *Src;
419 UINTN Nbr;
420 unsigned long Color;
421
422 //
423 // Check bounds
424 //
425 if (BltOperation == EfiUgaVideoToBltBuffer
426 || BltOperation == EfiUgaVideoToVideo) {
427 //
428 // Source is Video.
429 //
430 if (Args->SourceY + Args->Height > Private->height) {
431 return EFI_INVALID_PARAMETER;
432 }
433
434 if (Args->SourceX + Args->Width > Private->width) {
435 return EFI_INVALID_PARAMETER;
436 }
437 }
438
439 if (BltOperation == EfiUgaBltBufferToVideo
440 || BltOperation == EfiUgaVideoToVideo
441 || BltOperation == EfiUgaVideoFill) {
442 //
443 // Destination is Video
444 //
445 if (Args->DestinationY + Args->Height > Private->height) {
446 return EFI_INVALID_PARAMETER;
447 }
448
449 if (Args->DestinationX + Args->Width > Private->width) {
450 return EFI_INVALID_PARAMETER;
451 }
452 }
453
454 switch (BltOperation) {
455 case EfiUgaVideoToBltBuffer:
456 Blt = (EFI_UGA_PIXEL *)((UINT8 *)BltBuffer + (Args->DestinationY * Args->Delta) + Args->DestinationX * sizeof (EFI_UGA_PIXEL));
457 Args->Delta -= Args->Width * sizeof (EFI_UGA_PIXEL);
458 for (SrcY = Args->SourceY; SrcY < (Args->Height + Args->SourceY); SrcY++) {
459 for (SrcX = Args->SourceX; SrcX < (Args->Width + Args->SourceX); SrcX++) {
460 *Blt++ = UgaColorToPixel(Private,
461 XGetPixel(Private->image, SrcX, SrcY));
462 }
463 Blt = (EFI_UGA_PIXEL *) ((UINT8 *) Blt + Args->Delta);
464 }
465 break;
466 case EfiUgaBltBufferToVideo:
467 Blt = (EFI_UGA_PIXEL *)((UINT8 *)BltBuffer + (Args->SourceY * Args->Delta) + Args->SourceX * sizeof (EFI_UGA_PIXEL));
468 Args->Delta -= Args->Width * sizeof (EFI_UGA_PIXEL);
469 for (DstY = Args->DestinationY; DstY < (Args->Height + Args->DestinationY); DstY++) {
470 for (DstX = Args->DestinationX; DstX < (Args->Width + Args->DestinationX); DstX++) {
471 XPutPixel(Private->image, DstX, DstY, UgaPixelToColor(Private, *Blt));
472 Blt++;
473 }
474 Blt = (EFI_UGA_PIXEL *) ((UINT8 *) Blt + Args->Delta);
475 }
476 break;
477 case EfiUgaVideoToVideo:
478 Dst = Private->image_data + (Args->DestinationX << Private->pixel_shift)
479 + Args->DestinationY * Private->line_bytes;
480 Src = Private->image_data + (Args->SourceX << Private->pixel_shift)
481 + Args->SourceY * Private->line_bytes;
482 Nbr = Args->Width << Private->pixel_shift;
483 if (Args->DestinationY < Args->SourceY) {
484 for (Index = 0; Index < Args->Height; Index++) {
485 memcpy (Dst, Src, Nbr);
486 Dst += Private->line_bytes;
487 Src += Private->line_bytes;
488 }
489 }
490 else {
491 Dst += (Args->Height - 1) * Private->line_bytes;
492 Src += (Args->Height - 1) * Private->line_bytes;
493 for (Index = 0; Index < Args->Height; Index++) {
494 //
495 // Source and Destination Y may be equal, therefore Dst and Src may
496 // overlap.
497 //
498 memmove (Dst, Src, Nbr);
499 Dst -= Private->line_bytes;
500 Src -= Private->line_bytes;
501 }
502 }
503 break;
504 case EfiUgaVideoFill:
505 Color = UgaPixelToColor(Private, *BltBuffer);
506 for (DstY = Args->DestinationY; DstY < (Args->Height + Args->DestinationY); DstY++) {
507 for (DstX = Args->DestinationX; DstX < (Args->Width + Args->DestinationX); DstX++) {
508 XPutPixel(Private->image, DstX, DstY, Color);
509 }
510 }
511 break;
512 default:
513 return EFI_INVALID_PARAMETER;
514 }
515
516 //
517 // Refresh screen.
518 //
519 switch (BltOperation) {
520 case EfiUgaVideoToVideo:
521 XCopyArea(Private->display, Private->win, Private->win, Private->gc,
522 Args->SourceX, Args->SourceY, Args->Width, Args->Height, Args->DestinationX, Args->DestinationY);
523 while (1) {
524 XEvent ev;
525
526 XNextEvent (Private->display, &ev);
527 HandleEvent(Private, &ev);
528 if (ev.type == NoExpose || ev.type == GraphicsExpose)
529 break;
530 }
531 break;
532 case EfiUgaVideoFill:
533 Color = UgaPixelToColor(Private, *BltBuffer);
534 XSetForeground(Private->display, Private->gc, Color);
535 XFillRectangle(Private->display, Private->win, Private->gc,
536 Args->DestinationX, Args->DestinationY, Args->Width, Args->Height);
537 XFlush(Private->display);
538 break;
539 case EfiUgaBltBufferToVideo:
540 Redraw(Private, Args->DestinationX, Args->DestinationY, Args->Width, Args->Height);
541 break;
542 default:
543 break;
544 }
545 return EFI_SUCCESS;
546 }
547
548 EFI_STATUS
549 UgaCreate (EFI_UNIX_UGA_IO_PROTOCOL **Uga, CONST CHAR16 *Title)
550 {
551 UGA_IO_PRIVATE *drv;
552 unsigned int border_width = 0;
553 char *display_name = NULL;
554 int title_len;
555
556 drv = (UGA_IO_PRIVATE *)calloc (1, sizeof (UGA_IO_PRIVATE));
557 if (drv == NULL)
558 return EFI_OUT_OF_RESOURCES;
559
560 #ifdef __APPLE__
561 //
562 //
563 //
564 EFI_STATUS EFIAPI GasketUgaClose (EFI_UNIX_UGA_IO_PROTOCOL *UgaIo);
565 EFI_STATUS EFIAPI GasketUgaSize (EFI_UNIX_UGA_IO_PROTOCOL *UgaIo, UINT32 Width, UINT32 Height);
566 EFI_STATUS EFIAPI GasketUgaCheckKey (EFI_UNIX_UGA_IO_PROTOCOL *UgaIo);
567 EFI_STATUS EFIAPI GasketUgaGetKey (EFI_UNIX_UGA_IO_PROTOCOL *UgaIo, EFI_INPUT_KEY *key);
568 EFI_STATUS EFIAPI GasketUgaBlt (
569 EFI_UNIX_UGA_IO_PROTOCOL *UgaIo,
570 IN EFI_UGA_PIXEL *BltBuffer OPTIONAL,
571 IN EFI_UGA_BLT_OPERATION BltOperation,
572 IN UGA_BLT_ARGS *Args
573 );
574
575 drv->UgaIo.UgaClose = GasketUgaClose;
576 drv->UgaIo.UgaSize = GasketUgaSize;
577 drv->UgaIo.UgaCheckKey = GasketUgaCheckKey;
578 drv->UgaIo.UgaGetKey = GasketUgaGetKey;
579 drv->UgaIo.UgaBlt = GasketUgaBlt;
580 #else
581 drv->UgaIo.UgaClose = UgaClose;
582 drv->UgaIo.UgaSize = UgaSize;
583 drv->UgaIo.UgaCheckKey = UgaCheckKey;
584 drv->UgaIo.UgaGetKey = UgaGetKey;
585 drv->UgaIo.UgaBlt = UgaBlt;
586 #endif
587
588
589
590 drv->key_count = 0;
591 drv->key_rd = 0;
592 drv->key_wr = 0;
593 drv->display = XOpenDisplay (display_name);
594 if (drv->display == NULL)
595 {
596 fprintf (stderr, "uga: cannot connect to X server %s\n",
597 XDisplayName (display_name));
598 free (drv);
599 return EFI_DEVICE_ERROR;
600 }
601 drv->screen = DefaultScreen (drv->display);
602 drv->visual = DefaultVisual (drv->display, drv->screen);
603 drv->win = XCreateSimpleWindow
604 (drv->display, RootWindow (drv->display, drv->screen),
605 0, 0, 4, 4, border_width,
606 WhitePixel (drv->display, drv->screen),
607 BlackPixel (drv->display, drv->screen));
608
609 drv->depth = DefaultDepth (drv->display, drv->screen);
610
611 /* Compute title len and convert to Ascii. */
612 for (title_len = 0; Title[title_len] != 0; title_len++)
613 ;
614 {
615 char title[title_len + 1];
616 int i;
617 for (i = 0; i < title_len; i++)
618 title[i] = Title[i];
619 title[i] = 0;
620
621 XStoreName (drv->display, drv->win, title);
622 }
623
624 XSelectInput (drv->display, drv->win, ExposureMask | KeyPressMask);
625
626 drv->gc = DefaultGC (drv->display, drv->screen);
627
628 *Uga = (EFI_UNIX_UGA_IO_PROTOCOL *)drv;
629 return EFI_SUCCESS;
630 }