GAL and IAL Engines
In the development of MiniGUI version 0.3.xx, we introduce the concepts of Graphics and Input Abstract Layer (GAL and IAL
). Abstract layer is similar to Linux kernel virtual file system. It defines a group of abstract interfaces that do not rely on any special hardware. All top-layer graphics operations and input handling are based on abstract interfaces. The code used to realize the abstract interfaces are called graphic engine and input engine, similar to the driver of operating system. Actually it is an object-oriented programming technology. By using this abstract interface, we can easily port MiniGUI to other operating systems. Generally speaking, Linux-based embedded system kernel will provide FrameBuffer
support, thus existed FBCON
graphics engine of MiniGUI can run on normal PC
, and also can run on special embedded systems. Therefore, we usually do not need to develop a graphics engines for special embedded devices, but use FBCON
graphics engine. At the same time, MiniGUI also offers Shadow and CommLCD
GAL
engines for different situations. There will be simple introduction in the first and second sections of this chapter.
Compared to graphics engine, it is more important to separate the bottom input handling and top input handling of MiniGUI. In Linux-based embedded systems, graphics engine can get from FrameBuffer
, while input device does not have the uniformed interface. We usually use keyboard and mouse on PC
, but we may only use touch screen and keypad on embedded systems. Considering this situation, we have to say that it is very important for MiniGUI to provide an abstract input layer.
Therefore, the following sections will introduce Custom IAL
interface of MiniGUI 3.0, and focus on how to develop an input engine (Custom IAL
).
Shadow NEWGAL
Engine
NEWGAL
EngineThe main functions of this engine are:
Support for asynchronous update of graphics device, such as
YUV
output, indirect access toFrameBuffer
, etc.Support for display modes of lower than 8-bpp on
NEWGAL
. At present, it can support all kinds of display modes ofQVFB
and all packed-pixel modes ofFrameBuffer
console.
Shadow engine uses the conception of sub-driver. MiniGUI uses the name of target board to determine which sub-driver to be included. Only one sub-driver can be included at one time, which is determined by the configuration option –-with-targetname.
Some child drivers are implemented in the Shadow engine now, for example:
--with-targetname=vfanvil: The sub-driver for
VisualFone
Anvil board. This sub-driver runs on ThreadX operation system.--with-targetname=qvfb: The sub-driver for all kinds of Linux
QVFB
display modes.--with-targetname=wvfb: The sub-driver for all kinds of Windows
QVFB
display modes.Without target (TARGET_UNKNOWN): The default sub-driver. This sub-driver operates similarly to the Dummy
GAL
engine. You can modify this sub-driver to implement operations on the underlayer graphics device.
When configure rotating screen, Shadow engine can be configured in MiniGUI.cfg
as follow:
[shadow]
real_engine=pc_xvfb
defaultmode=800x600-16bpp
rotate_screen=normal
real_engine
Real engine, it means the name of device which draws graphic. Here ispc_xvfb
defaultmode Default display mode
rotate_screen
Whether or not rotate the screen when virtual screen is draw on real screen. If so, how to rotate. It has the following values:normal Don't rotate
cw Rotate it 90 degrees clockwise
ccw Rotate it 90 degrees counterclockwise
hflip Flip horizontal
vflip Flip vertical
When configure N (N is lower than 8) bits per pixel display mode, Shadow engine can be configured in MiniGUI.cfg
as follow:
[pc_xvfb]
defaultmode=800x600-1bpp
[shadow]
real_engine=pc_xvfb
defaultmode=800x600-8bpp
rotate_screen=normal
shadow engine's color depth is greater than 8, so real engine's color depth configuration (more than or equal to 8) is real color depth.
MLShadow
GAL
Engine
MLShadow
GAL
EngineMLShadow
engine implements multi-display-layer in many product solutions, such as set-top box and PMP
. Multi-display-layer is similar with overlay display layer which is provided by hardware, it can implement transparence/translucent display between layers.
MiniGUI provides the operation functions on virtual graphic layer in MLShadow
engine to implement controls for different graphic layers.
BOOL mlsSetSlaveScreenInfo (HDC dc_mls, DWORD mask, int offset_x, int offset_y,
DWORD blend_flags, gal_pixel color_key, int alpha, int z_order);
This function can get corresponding graphic layer, and set this graphic layer's offset, blend flag, transparent color, alpha and overlay order relative to real graphic layer by given dc_mls
argument. The details of arguments are as follow:
dc_mls
the correspondingDC
handler of graphic layermask To determine which properties should be set, ignore passed other properties's value. For example, only set
offset_x
andoffset_y
when mask =MLS_INFOMASK_OFFSET
; set all properties when mask isMLS_INFOMASK_ALL
. The range of mask's value is:MLS_INFOMASK_OFFSET
only set offset of display layerMLS_INFOMASK_BLEND
only set blend flag of display layerMLS_INFOMASK_ZORDER
only set overlay order of display layersMLS_INFOMASK_ENABLE
only set whether display layer is visibleMLS_INFOMASK_ALL
set all propertiesoffset_x
andoffset_y
set offset of given display layer on main screenblend_flag
To show the display layer is transparent or has alpha channel, flags can be:MLS_BLENDMODE_COLORKEY
Remove transparent color when blending.MLS_BLENDMODE_ALPHA
Remove alpha blend when blending.color_key
value of transparent coloralpha value of alpha channel
z_order
overlay order,z_order
is greater, display layer is wider.
BOOL mlsGetSlaveScreenInfo (HDC dc_mls, DWORD mask, int *offset_x, int *offset_y,
DWORD *blend_flags, gal_pixel *color_key, int *alpha, int *z_order);
This function gets overlay information of display layer which is given by dc_mls.
mask means to get all or partial information, its value has been explained in argument description of mlsSetSlaveScreenInfo
.
BOOL mlsEnableSlaveScreen (HDC dc_mls, BOOL enable);
This function set whether display layer which is give by dc_mls
is visible. In other words, it means whether or not participate overlay operation of display layer. It is visible (invisible) when enable is TRUE
(FALSE).
pc_xvfb
GAL
Engine
pc_xvfb
GAL
EngineThis engine's main functions are:
Using unified parameters startup options, so MiniGUI can start xvfb automatically when it is initializing.
Using unified shared memory ???, so
pc_xvfb
engine can use same key value to get framebuffer's system shared memory instance without caring about using Qt vfb, Window vfb or Gtk+ vfb.Using unified pipe/Unix socket object and input data transfer protocol, so
pc_xvfb
input engine can use same mechanism to get the data which is entered by user in the window without caring about using Qt vfb, Window vfb or Gtk+ vfb.
Configurations of pc_xvfb
engine in MiniGUI.cfg
are as follow:
[pc_xvfb]
defaultmode=800x600-16bpp
window_caption=XVFB-for-MiniGUI-3.0-(Qt-Version)
exec_file=/usr/local/bin/qvfb2
defaultmode Default display mode
window_caption
The text of window captionexec_file
Executable file of virtual framebuffer program. For qvfb, it is qvfb2. wvfb2 is for wvfb. mvfb2 is for mvfb.
rtos_xvfb
GAL
Engine
rtos_xvfb
GAL
EngineBy using of rtos_xvfb
engine, we can run MiniGUI on an exist GUI of RTOS, such as uC/GUI
, Gtk+ and Tilcon. The principle is as follows:
rtos_xvfb
program model creates window, allocates virtual frame buffer, and starts MiniGUI application with thread mode, so that MiniGUI application can be draw on virtual frame buffer.
APIs
of Creating and destroying virtual frame buffer are as follow:
XVFBHeader* xVFBAllocVirtualFrameBuffer (int width, int height, int depth,
Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask, BOOL MSBLeft);
void xVFBFreeVirtualFrameBuffer (XVFBHeader *fb);
Store input data which is generated by
rtos_xvfb
engine in ring buffer refers to agreed standard.rtos_xvfb
input engine of MiniGUI provides related interfaces:The interfaces of creating and destroying ring buffer
void* xVFBCreateEventBuffer (int nr_events);
void xVFBDestroyEventBuffer (void* buf);
Put input event into ring buffer and notify
rtos_xvfb
engine to read function interface.
int xVFBNotifyNewEvent (const void* xvfb_event_buffer, XVFBEVENT* event);
The configuration of rtos_xvfb
in MiniGUI.cfg
is quite simple. Only need to set default display mode as follow:
[rtos_xvfb]
defaultmode=800x600-16bpp
CommLCD
NEWGAL
Engine
CommLCD
NEWGAL
EngineThis engine provides the support for direct access to LCD
FrameBuffer
(video memory) on conditional real-time operation systems like VxWorks, Nucleus, uC/OS-II, and eCos
. The LCD
’s pixel format should be above 8-bpp (bits-per-pixel), and in packed-pixel mode.
CommLCD
engine also uses the conception of sub-driver. The sub-drivers already implemented in CommLCD
engine include:
--with-targetname=vxi386/vxppc (
__TARGET_VXi386__
and__TARGET_VXPPC__
): The sub-driver for VxWorks i386/PowerPc target.--with-targetname=c33l05(
__TARGET_C33L05__
):The sub-driver forEPSON
C33L05 target.--with-targetname=mx21(
__TARGET_MX21__
):The sub-driver forOSE
mx21 target.Without target (
__TARGET_UNKNOWN__
): The default sub-driver. If it iseCos
operation system, MiniGUI will use the standard interfaces to implement the sub-driver. Otherwise, you should define the sub-driver by yourself. For more information, please seesrc/newgal/commlcd/unknown.c
. There is a default implementation for uC/OS-II inrtos/ucos2_startup.c
and it is similar to dummy graphics engine.
In every sub-drive of CommLCD
, we need to implement the following interfaces according to the operating system and lower-level API.
int __commlcd_drv_init (void);
int __commlcd_drv_getinfo (struct commlcd_info *li);
int __commlcd_drv_release (void);
int __commlcd_drv_setclut (int firstcolor, int ncolors, GAL_Color *colors);
__commlcd_drv_init
function is used to initialize theLCD
device.__commlcd_drv_release
function is used to release theLCD
device.__commlcd_drv_getinfo
function is used to get information of theLCD
device.__commlcd_drv_setclut
is used to set color palette.
The structure commlcd_info
is defined as follows:
struct commlcd_info {
short height, width; // Pixels
short bpp; // Depth (bits-per-pixel)
short type; // Pixel type
short rlen; // Length of one raster line in bytes
void *fb; // Address of the frame buffer
};
Sub driver implementation sample is as follow:
……
struct commlcd_info {
short height, width; // Pixels
short bpp; // Depth (bits-per-pixel)
short type; // Pixel type
short rlen; // Length of one raster line in bytes
void *fb; // Address of the frame buffer
};
int __commlcd_drv_init (void)
{
if (uglInitialize() == UGL_STATUS_ERROR)
return 1;
return 0;
}
int __commlcd_drv_getinfo (struct commlcd_info *li)
{
UGL_MODE_INFO modeInfo;
/* Obtain display device identifier */
devId = (UGL_DEVICE_ID) uglRegistryFind (UGL_DISPLAY_TYPE,
0, 0, 0)->id;
/* Create a graphics context */
gc = uglGcCreate (devId);
……
uglPcBiosInfo (devId, UGL_MODE_INFO_REQ, &modeInfo);
li->type = COMMLCD_TRUE_RGB565;
li->height = modeInfo.height;
li->width = modeInfo.width;
li->fb = modeInfo.fbAddress;
li->bpp = modeInfo.colorDepth;
li->rlen = (li->bpp*li->width + 7) / 8;
return 0;
}
int __commlcd_drv_release (void)
{
return 0;
}
int __commlcd_drv_setclut (int firstcolor, int ncolors, GAL_Color *colors)
{
return 0;
}
……
Comm Input Engine
MiniGUI provides comm IAL
for conditional real-time operation systems like VxWorks, Nucleus, uC/OS-II, and eCos
. Based on this engine, you can easily add the support for input device such as keyboard, mouse, and touch screen.
The comm ial engine needs the OS or low-level device driver to provide five functions as follows:
int __comminput_init (void);
void __comminput_deinit (void);
int __comminput_ts_getdata (short *x, short *y, short *button);
int __comminput_kb_getdata (short *key, short *status);
int __comminput_wait_for_input (void);
__comminput_init
is used to initialize the input device.__comminput_deinit
is used to release the input device.__comminput_ts_getdata
get the input data of the touch screen. The "x" and "y" returns the position data, and "button" returns the pressed state (if pen is pressed, return a non-zero value). It returns 0 while getting data successfully, otherwise returns -1.__comminput_kb_getdata
gets the input data of the keyboard. "key" returns the key code of corresponding key; "status" returns the key status (1 for key-down, 0 for key-up).__comminput_kb_getdata
returns 0 while getting data successfully, otherwise returns -1. The key code here is theMiniGUI-defined
scan code of a keyboard key. The low-level keyboard driver needs to translate a keyboard scan code to a corresponding MiniGUI key code and return this key code.__comminput_wait_for_input
enquires whether there are input data. If no input events comes, this function returns 0; if mouse events or touch screen events comes, the return value's first position is set to 1; if keyboard events comes, the return value's second position is set to 1. The control of the external events in MiniGUI is implemented as a single system thread. This event thread sleeps while no external events come. So,__comminput_wait_for_input
should provide a waiting mechanism, such as using semaphore.__comminput_wait_for_input
waits on an input semaphore while enquiring about input data, and makes the MiniGUI input task which calls this function go to sleep. When input events come, the low-level drivers (or interrupt routines) should post a semaphore to wake up the MiniGUI event task.
When migrating MiniGUI to new hardware, we need to implement the above five functions interface according to OS or hardware driver.
Custom input engine provides user message handle interfaces of customized keyboard and mouse. User can handle message after implements the following interfaces:
BOOL InitCustomInput (INPUT* input, const char* mdev, const char* mtype);
void TermCustomInput (void);
When we need to customize input engine for specified embedded device, firstly, set value for member of input engine structure in initializing function. These members are function pointers, they are called by MiniGUI
's upper-level to get input device's status and data. Definition of INPUT
structure is as follow:
typedef struct tagINPUT
{
char* id;
// Initialization and termination
BOOL (*init_input) (struct tagINPUT *input, const char* mdev, const char* mtype);
void (*term_input) (void);
// Mouse operations
int (*update_mouse) (void);
void (*get_mouse_xy) (int* x, int* y);
void (*set_mouse_xy) (int x, int y);
int (*get_mouse_button) (void);
void (*set_mouse_range) (int minx, int miny, int maxx, int maxy);
void (*suspend_mouse) (void);
int (*resume_mouse) (void);
// Keyboard operations
int (*update_keyboard) (void);
const char* (*get_keyboard_state) (void);
void (*suspend_keyboard) (void);
int (*resume_keyboard) (void);
void (*set_leds) (unsigned int leds);
// Event
int (*wait_event) (int which, int maxfd, fd_set *in, fd_set *out,
fd_set *except, struct timeval *timeout);
char mdev [MAX_PATH + 1];
}INPUT;
These members' function are:
update_mouse
inform bottom-layer engine to update new mouse informationget_mouse_xy
calling this function by upper-layer to get x and y coordinates of mouseset_mouse_xy
calling function by upper-layer to set the new mouse position. For those engines not supporting this function, the member can be null.get_mouse_button
get mouse button state. Return value can beIAL_MOUSE_LEFTBUTTON
,IAL_MOUSE_MIDDLEBUTTON
,IAL_MOUSE_RIGHTBUTTON
to respectively represent the pressed states: mouse left key, middle key, and right key.set_mouse_range
set range of mouse movement. For engines not supporting this function, can be set asNULL
.update_keyboard
inform bottom-layer to update keyboard informationget_keyboard_state
get keyboard state, return a byte array, including keyboard pressed state indexed by scan code. Pressed is 1, released is 0.suspend_keyboard
pause keyboard device read/write, used for switch of virtual console. Usually set asNULL
for embedded device.resume_keyboard
resume keyboard device read/write, used for switch of virtual console. Usually set asNULL
for embedded device.set_leds
set keyboard statusLEDs
, used forCapLock
,NumLock
, andScrollLock
statues.wait_event
calling this function by upper-layer to wait for an event from input devices. This function has different interfaces for MiniGUI-Threads and MiniGUI-Processes, and must implement with select or poll system calls.
Custom IAL
engine sample
IAL
engine sampleActually developing a new IAL
engine is not difficult. We use iPAQ
as an example to illustrate the design of a customized input engine.
IPAQ
produced by COMPAQ
is a StrongARM-based
high-end hand-held product, which includes touch screen and several control keys. The touch screen is similar to the mouse of PC
, but it can only differentiate left button. For the control keys, we can emulate them as some keys in PC
keyboard, such as cursor keys, ENTER
key, and function keys. The source code of this engine is showed in List 1.
List 2 The customized input engine for iPAQ
(custom IAL
)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <linux/kd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <minigui/mgconfig.h>
#include <minigui/common.h>
#include <minigui/customial.h>
#ifdef _MGIAL_CUSTOM
/* for data reading from /dev/hs3600_ts */
typedef struct {
unsigned short b;
unsigned short x;
unsigned short y;
unsigned short pad;
} POS;
static unsigned char state [NR_KEYS];
static int ts = -1;
static int btn_fd = -1;
static unsigned char btn_state=0;
static int mousex = 0;
static int mousey = 0;
static POS pos;
#undef _DEBUG
/************************ Low Level Input Operations **********************/
/*
* Mouse operations -- Event
*/
static int mouse_update(void)
{
return 1;
}
static void mouse_getxy(int *x, int* y)
{
#ifdef _DEBUG
printf ("mousex = %d, mousey = %d\n", mousex, mousey);
#endif
if (mousex < 0) mousex = 0;
if (mousey < 0) mousey = 0;
if (mousex > 319) mousex = 319;
if (mousey > 239) mousey = 239;
#ifdef _COOR_TRANS
#if _ROT_DIR_CCW
*x = mousey;
*y = 319 - mousex;
#else
*x = 239 - mousey;
*y = mousex;
#endif
#else
*x = mousex;
*y = mousey;
#endif
}
static int mouse_getbutton(void)
{
return pos.b;
}
static int keyboard_update(void)
{
char *statinfo;
int status;
int key;
//Attention!
statinfo = (btn_state & KEY_RELEASED)? "UP":"DOWN";
status = (btn_state & KEY_RELEASED)? 0 : 1;
key = btn_state & KEY_NUM;
#ifdef _DEBUG
fprintf(stderr, "key %d is %s", key, statinfo);
#endif
switch (key)
{
case 1:
//state[H3600_SCANCODE_RECORD] = status;
state[SCANCODE_LEFTSHIFT] = status;
break;
case 2:
state[H3600_SCANCODE_CALENDAR] = status;
break;
case 3:
state[H3600_SCANCODE_CONTACTS] = status;
break;
case 4:
state[H3600_SCANCODE_Q] = status;
break;
case 5:
state[H3600_SCANCODE_START] = status;
break;
case 6:
state[H3600_SCANCODE_UP] = status;
break;
case 7:
state[H3600_SCANCODE_RIGHT] = status;
break;
case 8:
state[H3600_SCANCODE_LEFT] = status;
break;
case 9:
state[H3600_SCANCODE_DOWN] = status;
break;
case 10:
state[H3600_SCANCODE_ACTION] = status;
break;
case 11:
state[H3600_SCANCODE_SUSPEND] = status;
break;
}
return NR_KEYS;
}
static const char* keyboard_getstate(void)
{
return (char *)state;
}
#ifdef _LITE_VERSION
static int wait_event (int which, int maxfd, fd_set *in, fd_set *out, fd_set *except,
struct timeval *timeout)
#else
static int wait_event (int which, fd_set *in, fd_set *out, fd_set *except,
struct timeval *timeout)
#endif
{
fd_set rfds;
int retvalue = 0;
int e;
if (!in) {
in = &rfds;
FD_ZERO (in);
}
if ((which & IAL_MOUSEEVENT) && ts >= 0) {
FD_SET (ts, in);
#ifdef _LITE_VERSION
if (ts > maxfd) maxfd = ts;
#endif
}
if ((which & IAL_KEYEVENT) && btn_fd >= 0){
FD_SET (btn_fd, in);
#ifdef _LITE_VERSION
if(btn_fd > maxfd) maxfd = btn_fd;
#endif
}
#ifdef _LITE_VERSION
e = select (maxfd + 1, in, out, except, timeout) ;
#else
e = select (FD_SETSIZE, in, out, except, timeout) ;
#endif
if (e > 0) {
if (ts >= 0 && FD_ISSET (ts, in))
{
FD_CLR (ts, in);
pos.x=0;
pos.y=0;
// FIXME: maybe failed due to the struct alignment.
read (ts, &pos, sizeof (POS));
//if (pos.x = -1 && pos.y = -1) {
if (pos.b > 0) {
mousex = pos.x;
mousey = pos.y;
}
//}
#ifdef _DEBUG
if (pos.b > 0) {
printf ("mouse down: pos.x = %d, pos.y = %d\n", pos.x, pos.y);
}
#endif
pos.b = ( pos.b > 0 ? 4:0);
retvalue |= IAL_MOUSEEVENT;
}
if (btn_fd >= 0 && FD_ISSET(btn_fd, in))
{
unsigned char key;
FD_CLR(btn_fd, in);
read(btn_fd, &key, sizeof(key));
btn_state = key;
retvalue |= IAL_KEYEVENT;
}
} else if (e < 0) {
return -1;
}
return retvalue;
}
BOOL InitCustomInput (INPUT* input, const char* mdev, const char* mtype)
{
ts = open ("/dev/h3600_ts", O_RDONLY);
if (ts < 0) {
fprintf (stderr, "IPAQ: Can not open touch screen!\n");
return FALSE;
}
btn_fd = open ("/dev/h3600_key", O_RDONLY);
if (btn_fd < 0 ) {
fprintf (stderr, "IPAQ: Can not open button key!\n");
return FALSE;
}
input->update_mouse = mouse_update;
input->get_mouse_xy = mouse_getxy;
input->set_mouse_xy = NULL;
input->get_mouse_button = mouse_getbutton;
input->set_mouse_range = NULL;
input->update_keyboard = keyboard_update;
input->get_keyboard_state = keyboard_getstate;
input->set_leds = NULL;
input->wait_event = wait_event;
mousex = 0;
mousey = 0;
pos.x = pos.y = pos.b = 0;
return TRUE;
}
void TermCustomInput (void)
{
if (ts >= 0)
close(ts);
if (btn_fd >= 0)
close(btn_fd);
}
#endif /* _MGIAL_CUSTOM */
We now analyze how some important interface functions implement:
The function
IInitCustomInput
is the initializing function of input engine defined in customial.h of MiniGUI 3.0. This function opens two devices: /dev/h3600_ts and /dev/h3600_key. The former is the device file for touch screen; the latter is the device for control keys. They are similar to device /dev/psaux and device /dev/tty onPCs
. After successfully opening these two device files, the function setsINPUT
structure and other members, some of which is assigned withNULL
.The function
mouse_update
returns 1, indicating that the mouse state is ready.The function
mouse_getxy
returns mouse position data prepared by other functions and performs proper boundary examination.The function
mouse_getbutton
returns touch screen state, that is, whether the user have touched the screen. It is similar to whether the user has pressed the left button of mouse.The function
keyboard_update
properly fills state array according to the keyboard information prepared by other functions.The function
keyboard_state
directly returns the address of state array.The function
wait_event
is the core function of the input engine. This function first combines the two opened device file descriptors within the descriptor set in. Then it calls select system call. When the value returned by select is more than 0, this function will examine if there is any readable data waiting to read in the two file descriptors, if so, it will read touch screen and key stroke data separately from the two file descriptors.
<< Developing Customized MiniGUI-Processes Server Program | Table of Contents | Universal Startup API for RTOSes >>
Last updated