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
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.
21 #include "Protocol/UnixThunk.h"
22 #include "Protocol/SimpleTextIn.h"
23 #include "Protocol/UgaDraw.h"
24 #include "Protocol/UnixUgaIo.h"
26 #include <X11/Xutil.h>
28 #include <X11/extensions/XShm.h>
29 #include <X11/keysym.h>
31 #include <Ppi/StatusCode.h>
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>
40 extern void msSleep (unsigned long Milliseconds
);
44 struct uga_drv_shift_mask
54 EFI_UNIX_UGA_IO_PROTOCOL UgaIo
;
57 int screen
; /* values for window_size in main */
65 unsigned int line_bytes
;
66 unsigned int pixel_shift
;
67 unsigned char *image_data
;
69 struct uga_drv_shift_mask r
, g
, b
;
72 XShmSegmentInfo xshm_info
;
77 unsigned int key_count
;
78 EFI_INPUT_KEY keys
[NBR_KEYS
];
82 HandleEvents(UGA_IO_PRIVATE
*drv
);
85 fill_shift_mask (struct uga_drv_shift_mask
*sm
, unsigned long mask
)
89 while ((mask
& 1) == 0)
99 sm
->csize
= 8 - sm
->size
;
103 TryCreateShmImage(UGA_IO_PRIVATE
*drv
)
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
)
111 switch (drv
->image
->bitmap_unit
) {
113 drv
->pixel_shift
= 2;
116 drv
->pixel_shift
= 1;
119 drv
->pixel_shift
= 0;
123 drv
->xshm_info
.shmid
= shmget
124 (IPC_PRIVATE
, drv
->image
->bytes_per_line
* drv
->image
->height
,
126 if (drv
->xshm_info
.shmid
< 0) {
127 XDestroyImage(drv
->image
);
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
);
140 // This closes shared memory in real time on OS X. Only closes after folks quit using
143 /* Can this fail ? */
144 shmctl (drv
->xshm_info
.shmid
, IPC_RMID
, NULL
);
147 drv
->xshm_info
.shmaddr
= (char*)drv
->image_data
;
148 drv
->image
->data
= (char*)drv
->image_data
;
150 if (!XShmAttach (drv
->display
, &drv
->xshm_info
)) {
151 shmdt (drv
->image_data
);
152 XDestroyImage(drv
->image
);
159 UgaClose (EFI_UNIX_UGA_IO_PROTOCOL
*UgaIo
)
161 UGA_IO_PRIVATE
*drv
= (UGA_IO_PRIVATE
*)UgaIo
;
165 if (drv
->image
!= NULL
)
167 XDestroyImage(drv
->image
);
170 shmdt (drv
->image_data
);
172 drv
->image_data
= NULL
;
175 XDestroyWindow(drv
->display
, drv
->win
);
176 XCloseDisplay(drv
->display
);
179 // Free up the shared memory
180 shmctl (drv
->xshm_info
.shmid
, IPC_RMID
, NULL
);
188 UgaSize(EFI_UNIX_UGA_IO_PROTOCOL
*UgaIo
, UINT32 Width
, UINT32 Height
)
190 UGA_IO_PRIVATE
*drv
= (UGA_IO_PRIVATE
*)UgaIo
;
191 XSizeHints size_hints
;
193 /* Destroy current buffer if created. */
194 if (drv
->image
!= NULL
)
196 /* Before destroy buffer, need to make sure the buffer available for access. */
197 XDestroyImage(drv
->image
);
200 shmdt (drv
->image_data
);
202 drv
->image_data
= NULL
;
207 drv
->height
= Height
;
208 XResizeWindow (drv
->display
, drv
->win
, Width
, Height
);
210 /* Allocate image. */
211 if (XShmQueryExtension(drv
->display
) && TryCreateShmImage(drv
)) {
216 drv
->pixel_shift
= 2;
217 else if (drv
->depth
> 8)
218 drv
->pixel_shift
= 1;
220 drv
->pixel_shift
= 0;
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);
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
);
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
);
239 XMapWindow (drv
->display
, drv
->win
);
245 handleKeyEvent(UGA_IO_PRIVATE
*drv
, XEvent
*ev
)
252 if (drv
->key_count
== NBR_KEYS
)
255 res
= XLookupString(&ev
->xkey
, str
, sizeof(str
), &keysym
, NULL
);
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;
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;
283 Key
.UnicodeChar
= str
[0];
289 drv
->keys
[drv
->key_wr
] = Key
;
290 drv
->key_wr
= (drv
->key_wr
+ 1) % NBR_KEYS
;
295 Redraw(UGA_IO_PRIVATE
*drv
, UINTN X
, UINTN Y
, UINTN Width
, UINTN Height
)
298 XShmPutImage (drv
->display
, drv
->win
, drv
->gc
, drv
->image
,
299 X
, Y
, X
, Y
, Width
, Height
, False
);
301 XPutImage (drv
->display
, drv
->win
, drv
->gc
, drv
->image
,
302 X
, Y
, X
, Y
, Width
, Height
);
303 XFlush(drv
->display
);
307 HandleEvent(UGA_IO_PRIVATE
*drv
, XEvent
*ev
)
312 Redraw(drv
, ev
->xexpose
.x
, ev
->xexpose
.y
,
313 ev
->xexpose
.width
, ev
->xexpose
.height
);
316 Redraw(drv
, ev
->xgraphicsexpose
.x
, ev
->xgraphicsexpose
.y
,
317 ev
->xgraphicsexpose
.width
, ev
->xgraphicsexpose
.height
);
320 handleKeyEvent(drv
, ev
);
323 XRefreshKeyboardMapping(&ev
->xmapping
);
327 XCloseDisplay (drv
->display
);
338 HandleEvents(UGA_IO_PRIVATE
*drv
)
340 while (XPending(drv
->display
) != 0)
344 XNextEvent (drv
->display
, &ev
);
345 HandleEvent(drv
, &ev
);
350 UgaPixelToColor (UGA_IO_PRIVATE
*drv
, EFI_UGA_PIXEL pixel
)
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
);
358 UgaColorToPixel (UGA_IO_PRIVATE
*drv
, unsigned long val
)
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
;
372 UgaCheckKey(EFI_UNIX_UGA_IO_PROTOCOL
*UgaIo
)
374 UGA_IO_PRIVATE
*drv
= (UGA_IO_PRIVATE
*)UgaIo
;
377 if (drv
->key_count
!= 0) {
380 /* EFI is certainly polling. Be CPU-friendly. */
382 return EFI_NOT_READY
;
387 UgaGetKey (EFI_UNIX_UGA_IO_PROTOCOL
*UgaIo
, EFI_INPUT_KEY
*key
)
389 UGA_IO_PRIVATE
*drv
= (UGA_IO_PRIVATE
*)UgaIo
;
392 status
= UgaCheckKey(UgaIo
);
393 if (status
!= EFI_SUCCESS
)
396 *key
= drv
->keys
[drv
->key_rd
];
397 drv
->key_rd
= (drv
->key_rd
+ 1) % NBR_KEYS
;
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
410 UGA_IO_PRIVATE
*Private
= (UGA_IO_PRIVATE
*)UgaIo
;
425 if (BltOperation
== EfiUgaVideoToBltBuffer
426 || BltOperation
== EfiUgaVideoToVideo
) {
430 if (Args
->SourceY
+ Args
->Height
> Private
->height
) {
431 return EFI_INVALID_PARAMETER
;
434 if (Args
->SourceX
+ Args
->Width
> Private
->width
) {
435 return EFI_INVALID_PARAMETER
;
439 if (BltOperation
== EfiUgaBltBufferToVideo
440 || BltOperation
== EfiUgaVideoToVideo
441 || BltOperation
== EfiUgaVideoFill
) {
443 // Destination is Video
445 if (Args
->DestinationY
+ Args
->Height
> Private
->height
) {
446 return EFI_INVALID_PARAMETER
;
449 if (Args
->DestinationX
+ Args
->Width
> Private
->width
) {
450 return EFI_INVALID_PARAMETER
;
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
));
463 Blt
= (EFI_UGA_PIXEL
*) ((UINT8
*) Blt
+ Args
->Delta
);
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
));
474 Blt
= (EFI_UGA_PIXEL
*) ((UINT8
*) Blt
+ Args
->Delta
);
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
;
491 Dst
+= (Args
->Height
- 1) * Private
->line_bytes
;
492 Src
+= (Args
->Height
- 1) * Private
->line_bytes
;
493 for (Index
= 0; Index
< Args
->Height
; Index
++) {
495 // Source and Destination Y may be equal, therefore Dst and Src may
498 memmove (Dst
, Src
, Nbr
);
499 Dst
-= Private
->line_bytes
;
500 Src
-= Private
->line_bytes
;
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
);
513 return EFI_INVALID_PARAMETER
;
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
);
526 XNextEvent (Private
->display
, &ev
);
527 HandleEvent(Private
, &ev
);
528 if (ev
.type
== NoExpose
|| ev
.type
== GraphicsExpose
)
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
);
539 case EfiUgaBltBufferToVideo
:
540 Redraw(Private
, Args
->DestinationX
, Args
->DestinationY
, Args
->Width
, Args
->Height
);
549 UgaCreate (EFI_UNIX_UGA_IO_PROTOCOL
**Uga
, CONST CHAR16
*Title
)
552 unsigned int border_width
= 0;
553 char *display_name
= NULL
;
556 drv
= (UGA_IO_PRIVATE
*)calloc (1, sizeof (UGA_IO_PRIVATE
));
558 return EFI_OUT_OF_RESOURCES
;
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
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
;
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
;
593 drv
->display
= XOpenDisplay (display_name
);
594 if (drv
->display
== NULL
)
596 fprintf (stderr
, "uga: cannot connect to X server %s\n",
597 XDisplayName (display_name
));
599 return EFI_DEVICE_ERROR
;
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
));
609 drv
->depth
= DefaultDepth (drv
->display
, drv
->screen
);
611 /* Compute title len and convert to Ascii. */
612 for (title_len
= 0; Title
[title_len
] != 0; title_len
++)
615 char title
[title_len
+ 1];
617 for (i
= 0; i
< title_len
; i
++)
621 XStoreName (drv
->display
, drv
->win
, title
);
624 XSelectInput (drv
->display
, drv
->win
, ExposureMask
| KeyPressMask
);
626 drv
->gc
= DefaultGC (drv
->display
, drv
->screen
);
628 *Uga
= (EFI_UNIX_UGA_IO_PROTOCOL
*)drv
;