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