/* * This file is subject to the terms of the GFX License. If a copy of * the license was not distributed with this file, you can obtain one at: * * http://ugfx.org/license.html */ // We need to include stdio.h below. Turn off GFILE_NEED_STDIO just for this file to prevent conflicts #define GFILE_NEED_STDIO_MUST_BE_OFF #include "gfx.h" #if GFX_USE_GDISP #include #include #include #include #include #define GDISP_DRIVER_VMT GDISPVMT_X11 #include "gdisp_lld_config.h" #include "../../../src/gdisp/gdisp_driver.h" // Configuration parameters for this driver #ifndef GDISP_FORCE_24BIT #define GDISP_FORCE_24BIT GFXOFF #endif #ifndef GDISP_SCREEN_WIDTH #define GDISP_SCREEN_WIDTH 640 #endif #ifndef GDISP_SCREEN_HEIGHT #define GDISP_SCREEN_HEIGHT 480 #endif #ifndef GKEYBOARD_X_NO_LAYOUT /** * Setting this to GFXON turns off the layout engine. * In this situation "cooked" characters are returned but * shift states etc are lost. * As only a limited number of keyboard layouts are currently * defined for X in uGFX (currently none), setting this * to GFXON enables the X keyboard mapping to be pass non-English * characters to uGFX or to handle non-standard keyboard layouts at * the expense of losing special function keys etc. */ // We set this to GFXON by default as currently the X layout code is not complete! #define GKEYBOARD_X_NO_LAYOUT GFXON #endif #ifndef GKEYBOARD_X_DEFAULT_LAYOUT #define GKEYBOARD_X_DEFAULT_LAYOUT KeyboardLayout_X_US #endif // Driver status flags #define GDISP_FLG_READY (GDISP_FLG_DRIVER<<0) #if GINPUT_NEED_MOUSE // Include mouse support code #define GMOUSE_DRIVER_VMT GMOUSEVMT_X11 #include "../../../src/ginput/ginput_driver_mouse.h" // Forward definitions static gBool XMouseInit(GMouse *m, unsigned driverinstance); static gBool XMouseRead(GMouse *m, GMouseReading *prd); const GMouseVMT const GMOUSE_DRIVER_VMT[1] = {{ { GDRIVER_TYPE_MOUSE, GMOUSE_VFLG_NOPOLL|GMOUSE_VFLG_DYNAMICONLY, // Extra flags for testing only //GMOUSE_VFLG_TOUCH|GMOUSE_VFLG_SELFROTATION|GMOUSE_VFLG_DEFAULTFINGER //GMOUSE_VFLG_CALIBRATE|GMOUSE_VFLG_CAL_EXTREMES|GMOUSE_VFLG_CAL_TEST|GMOUSE_VFLG_CAL_LOADFREE //GMOUSE_VFLG_ONLY_DOWN|GMOUSE_VFLG_POORUPDOWN sizeof(GMouse), _gmouseInitDriver, _gmousePostInitDriver, _gmouseDeInitDriver }, 1, // z_max 0, // z_min 1, // z_touchon 0, // z_touchoff { // pen_jitter 0, // calibrate 0, // click 0 // move }, { // finger_jitter 0, // calibrate 2, // click 2 // move }, XMouseInit, // init 0, // deinit XMouseRead, // get 0, // calsave 0 // calload }}; #endif #if GINPUT_NEED_KEYBOARD // Include mouse support code #define GKEYBOARD_DRIVER_VMT GKEYBOARDVMT_X #include "../../../src/ginput/ginput_driver_keyboard.h" #if !GKEYBOARD_X_NO_LAYOUT #if GKEYBOARD_LAYOUT_OFF #error "The X keyboard driver is using the layout engine. Please set GKEYBOARD_LAYOUT_OFF=GFXOFF or GKEYBOARD_X_NO_LAYOUT=GFXON." #endif // Forward definitions extern uint8_t GKEYBOARD_X_DEFAULT_LAYOUT[]; #include "../../../src/ginput/ginput_keyboard_microcode.h" #include // This is the layout code for the English US keyboard. // We make it public so that a user can switch to a different layout if required. uint8_t KeyboardLayout_X_US[] = { KMC_HEADERSTART, KMC_HEADER_ID1, KMC_HEADER_ID2, KMC_HEADER_VER_1, // TODO #error "The code to do keyboard layouts in X is not complete." // Transient Shifters: SHIFT, CTRL, ALT, WINKEY // Locking Shifters: CAPSLOCK, NUMLOCK and SCROLLLOCK // Keyup, Repeat // 0 - 9 // A - Z // Number pad // Symbols // Special Keys // Anything else // EOF KMC_RECORDSTART, 0 }; #elif !GKEYBOARD_LAYOUT_OFF #if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT #warning "The X keyboard driver is not using the layout engine. If no other keyboard is using it consider defining GKEYBOARD_LAYOUT_OFF=GFXON to save code size." #elif GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_MACRO COMPILER_WARNING("The X keyboard driver is not using the layout engine. If no other keyboard is using it consider defining GKEYBOARD_LAYOUT_OFF=GFXON to save code size.") #endif #endif // Forward definitions static gBool XKeyboardInit(GKeyboard *k, unsigned driverinstance); static int XKeyboardGetData(GKeyboard *k, uint8_t *pch, int sz); const GKeyboardVMT const GKEYBOARD_DRIVER_VMT[1] = {{ { GDRIVER_TYPE_KEYBOARD, GKEYBOARD_VFLG_NOPOLL, // GKEYBOARD_VFLG_DYNAMICONLY sizeof(GKeyboard), _gkeyboardInitDriver, _gkeyboardPostInitDriver, _gkeyboardDeInitDriver }, // The default keyboard layout #if GKEYBOARD_X_NO_LAYOUT 0, #else GKEYBOARD_X_DEFAULT_LAYOUT, #endif XKeyboardInit, // init 0, // deinit XKeyboardGetData, // getdata 0 // putdata void (*putdata)(GKeyboard *k, char ch); Optional }}; static int keypos; static uint8_t keybuffer[8]; static GKeyboard *keyboard; #endif static gBool initdone; static Display *dis; static int scr; static XEvent evt; static Colormap cmap; static XVisualInfo vis; static XContext cxt; static Atom wmDelete; typedef struct xPriv { Pixmap pix; GC gc; Window win; #if GINPUT_NEED_MOUSE gCoord mousex, mousey; uint16_t buttons; GMouse * mouse; #endif } xPriv; static void ProcessEvent(GDisplay *g, xPriv *priv) { switch(evt.type) { case MapNotify: XSelectInput(dis, evt.xmap.window, StructureNotifyMask|ExposureMask|ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|KeymapStateMask); g->flags |= GDISP_FLG_READY; break; case UnmapNotify: XCloseDisplay(dis); exit(0); break; case ClientMessage: if ((Atom)evt.xclient.data.l[0] == wmDelete) { XCloseDisplay(dis); exit(0); } break; case Expose: XCopyArea(dis, priv->pix, evt.xexpose.window, priv->gc, evt.xexpose.x, evt.xexpose.y, evt.xexpose.width, evt.xexpose.height, evt.xexpose.x, evt.xexpose.y); break; #if GINPUT_NEED_MOUSE case ButtonPress: priv->mousex = evt.xbutton.x; priv->mousey = evt.xbutton.y; switch(evt.xbutton.button){ case 1: priv->buttons |= GINPUT_MOUSE_BTN_LEFT; break; case 2: priv->buttons |= GINPUT_MOUSE_BTN_MIDDLE; break; case 3: priv->buttons |= GINPUT_MOUSE_BTN_RIGHT; break; case 4: priv->buttons |= GINPUT_MOUSE_BTN_4; break; } _gmouseWakeup(priv->mouse); break; case ButtonRelease: priv->mousex = evt.xbutton.x; priv->mousey = evt.xbutton.y; switch(evt.xbutton.button){ case 1: priv->buttons &= ~GINPUT_MOUSE_BTN_LEFT; break; case 2: priv->buttons &= ~GINPUT_MOUSE_BTN_MIDDLE; break; case 3: priv->buttons &= ~GINPUT_MOUSE_BTN_RIGHT; break; case 4: priv->buttons &= ~GINPUT_MOUSE_BTN_4; break; } _gmouseWakeup(priv->mouse); break; case MotionNotify: priv->mousex = evt.xmotion.x; priv->mousey = evt.xmotion.y; _gmouseWakeup(priv->mouse); break; #endif #if GINPUT_NEED_KEYBOARD case KeymapNotify: XRefreshKeyboardMapping(&evt.xmapping); break; case KeyPress: #if !GKEYBOARD_X_NO_LAYOUT // TODO #error "The code to do keyboard layouts in X is not complete." #endif /* ignore these when there is no layout engine */ break; case KeyRelease: #if !GKEYBOARD_X_NO_LAYOUT // TODO #error "The code to do keyboard layouts in X is not complete." #endif if (keyboard && !keyboard->pLayout && keypos < (int)sizeof(keybuffer)) { int len; len = XLookupString(&evt.xkey, (char *)(keybuffer+keypos), sizeof(keybuffer)-keypos, /*&keysym*/0, NULL); if (len > 0) { keypos += len; _gkeyboardWakeup(keyboard); } } break; #endif } } /* this is the X11 thread which keeps track of all events */ static DECLARE_THREAD_STACK(waXThread, 1024); static DECLARE_THREAD_FUNCTION(ThreadX, arg) { GDisplay *g; (void)arg; while(1) { gfxSleepMilliseconds(100); while(XPending(dis)) { XNextEvent(dis, &evt); XFindContext(evt.xany.display, evt.xany.window, cxt, (XPointer*)&g); ProcessEvent(g, (xPriv *)g->priv); } } return 0; } static int FatalXIOError(Display *d) { (void) d; /* The window has closed */ fprintf(stderr, "GFX Window closed!\n"); exit(0); } LLDSPEC gBool gdisp_lld_init(GDisplay *g) { XSizeHints *pSH; XSetWindowAttributes xa; XTextProperty WindowTitle; char * WindowTitleText; xPriv *priv; if (!initdone) { gfxThreadHandle hth; initdone = gTrue; #if GFX_USE_OS_LINUX || GFX_USE_OS_OSX XInitThreads(); #endif dis = XOpenDisplay(0); scr = DefaultScreen(dis); cxt = XUniqueContext(); wmDelete = XInternAtom(dis, "WM_DELETE_WINDOW", False); XSetIOErrorHandler(FatalXIOError); #if GDISP_FORCE_24BIT if (!XMatchVisualInfo(dis, scr, 24, TrueColor, &vis)) { fprintf(stderr, "Your display has no TrueColor mode\n"); XCloseDisplay(dis); return gFalse; } cmap = XCreateColormap(dis, RootWindow(dis, scr), vis.visual, AllocNone); #else vis.visual = CopyFromParent; vis.depth = DefaultDepth(dis, scr); cmap = DefaultColormap(dis, scr); #endif fprintf(stderr, "Running GFX Window in %d bit color\n", vis.depth); if (!(hth = gfxThreadCreate(waXThread, sizeof(waXThread), gThreadpriorityHigh, ThreadX, 0))) { fprintf(stderr, "Cannot start X Thread\n"); XCloseDisplay(dis); exit(0); } #if GFX_USE_OS_LINUX || GFX_USE_OS_OSX pthread_detach(hth); #endif gfxThreadClose(hth); } g->priv = gfxAlloc(sizeof(xPriv)); priv = (xPriv *)g->priv; g->board = 0; // No board interface for this driver xa.colormap = cmap; xa.border_pixel = 0xFFFFFF; xa.background_pixel = 0x000000; priv->win = XCreateWindow(dis, RootWindow(dis, scr), 16, 16, GDISP_SCREEN_WIDTH, GDISP_SCREEN_HEIGHT, 0, vis.depth, InputOutput, vis.visual, CWBackPixel|CWColormap|CWBorderPixel, &xa); XSync(dis, TRUE); XSaveContext(dis, priv->win, cxt, (XPointer)g); XSetWMProtocols(dis, priv->win, &wmDelete, 1); { char buf[132]; sprintf(buf, "uGFX - %u", g->systemdisplay+1); WindowTitleText = buf; XStringListToTextProperty(&WindowTitleText, 1, &WindowTitle); XSetWMName(dis, priv->win, &WindowTitle); XSetWMIconName(dis, priv->win, &WindowTitle); XSync(dis, TRUE); } pSH = XAllocSizeHints(); pSH->flags = PSize | PMinSize | PMaxSize; pSH->min_width = pSH->max_width = pSH->base_width = GDISP_SCREEN_WIDTH; pSH->min_height = pSH->max_height = pSH->base_height = GDISP_SCREEN_HEIGHT; XSetWMNormalHints(dis, priv->win, pSH); XFree(pSH); XSync(dis, TRUE); priv->pix = XCreatePixmap(dis, priv->win, GDISP_SCREEN_WIDTH, GDISP_SCREEN_HEIGHT, vis.depth); XSync(dis, TRUE); priv->gc = XCreateGC(dis, priv->win, 0, 0); XSetBackground(dis, priv->gc, BlackPixel(dis, scr)); XSync(dis, TRUE); // Create the associated mouse before the map #if GINPUT_NEED_MOUSE priv->mouse = (GMouse *)gdriverRegister((const GDriverVMT const *)GMOUSE_DRIVER_VMT, g); #endif XSelectInput(dis, priv->win, StructureNotifyMask); XMapWindow(dis, priv->win); // Wait for the window creation to complete (for safety) while(!(((volatile GDisplay *)g)->flags & GDISP_FLG_READY)) gfxSleepMilliseconds(100); /* Initialise the GDISP structure to match */ g->g.Orientation = gOrientation0; g->g.Powermode = gPowerOn; g->g.Backlight = 100; g->g.Contrast = 50; g->g.Width = GDISP_SCREEN_WIDTH; g->g.Height = GDISP_SCREEN_HEIGHT; return gTrue; } LLDSPEC void gdisp_lld_draw_pixel(GDisplay *g) { xPriv * priv = (xPriv *)g->priv; XColor col; col.red = RED_OF(g->p.color) << 8; col.green = GREEN_OF(g->p.color) << 8; col.blue = BLUE_OF(g->p.color) << 8; XAllocColor(dis, cmap, &col); XSetForeground(dis, priv->gc, col.pixel); XDrawPoint(dis, priv->pix, priv->gc, (int)g->p.x, (int)g->p.y ); XDrawPoint(dis, priv->win, priv->gc, (int)g->p.x, (int)g->p.y ); XFlush(dis); } #if GDISP_HARDWARE_FILLS LLDSPEC void gdisp_lld_fill_area(GDisplay *g) { xPriv * priv = (xPriv *)g->priv; XColor col; col.red = RED_OF(g->p.color) << 8; col.green = GREEN_OF(g->p.color) << 8; col.blue = BLUE_OF(g->p.color) << 8; XAllocColor(dis, cmap, &col); XSetForeground(dis, priv->gc, col.pixel); XFillRectangle(dis, priv->pix, priv->gc, g->p.x, g->p.y, g->p.cx, g->p.cy); XFillRectangle(dis, priv->win, priv->gc, g->p.x, g->p.y, g->p.cx, g->p.cy); XFlush(dis); } #endif #if 0 && GDISP_HARDWARE_BITFILLS LLDSPEC void gdisp_lld_blit_area(GDisplay *g) { // Start of Bitblit code //XImage bitmap; //gPixel *bits; // bits = malloc(vis.depth * GDISP_SCREEN_WIDTH * GDISP_SCREEN_HEIGHT); // bitmap = XCreateImage(dis, vis, vis.depth, ZPixmap, // 0, bits, GDISP_SCREEN_WIDTH, GDISP_SCREEN_HEIGHT, // 0, 0); } #endif #if GDISP_HARDWARE_PIXELREAD LLDSPEC gColor gdisp_lld_get_pixel_color(GDisplay *g) { xPriv * priv = (xPriv *)g->priv; XColor color; XImage *img; img = XGetImage (dis, priv->pix, g->p.x, g->p.y, 1, 1, AllPlanes, XYPixmap); color.pixel = XGetPixel (img, 0, 0); XFree(img); XQueryColor(dis, cmap, &color); return RGB2COLOR(color.red>>8, color.green>>8, color.blue>>8); } #endif #if GDISP_NEED_SCROLL && GDISP_HARDWARE_SCROLL LLDSPEC void gdisp_lld_vertical_scroll(GDisplay *g) { xPriv * priv = (xPriv *)g->priv; if (g->p.y1 > 0) { XCopyArea(dis, priv->pix, priv->pix, priv->gc, g->p.x, g->p.y+g->p.y1, g->p.cx, g->p.cy-g->p.y1, g->p.x, g->p.y); XCopyArea(dis, priv->pix, priv->win, priv->gc, g->p.x, g->p.y, g->p.cx, g->p.cy-g->p.y1, g->p.x, g->p.y); } else { XCopyArea(dis, priv->pix, priv->pix, priv->gc, g->p.x, g->p.y, g->p.cx, g->p.cy+g->p.y1, g->p.x, g->p.y-g->p.y1); XCopyArea(dis, priv->pix, priv->win, priv->gc, g->p.x, g->p.y-g->p.y1, g->p.cx, g->p.cy+g->p.y1, g->p.x, g->p.y-g->p.y1); } } #endif #if GINPUT_NEED_MOUSE static gBool XMouseInit(GMouse *m, unsigned driverinstance) { (void) m; (void) driverinstance; return gTrue; } static gBool XMouseRead(GMouse *m, GMouseReading *pt) { xPriv * priv; priv = m->display->priv; pt->x = priv->mousex; pt->y = priv->mousey; pt->z = (priv->buttons & GINPUT_MOUSE_BTN_LEFT) ? 1 : 0; pt->buttons = priv->buttons; return gTrue; } #endif /* GINPUT_NEED_MOUSE */ #if GINPUT_NEED_KEYBOARD static gBool XKeyboardInit(GKeyboard *k, unsigned driverinstance) { (void) driverinstance; // Only one please if (keyboard) return gFalse; keyboard = k; return gTrue; } static int XKeyboardGetData(GKeyboard *k, uint8_t *pch, int sz) { int i, j; (void) k; if (!keypos) return 0; for(i = 0; i < keypos && i < sz; i++) pch[i] = keybuffer[i]; keypos -= i; for(j=0; j < keypos; j++) keybuffer[j] = keybuffer[i+j]; return i; } #endif /* GINPUT_NEED_KEYBOARD */ #endif /* GFX_USE_GDISP */