diff options
Diffstat (limited to 'drivers/multiple/Win32/gdisp_lld_Win32.c')
-rw-r--r-- | drivers/multiple/Win32/gdisp_lld_Win32.c | 1163 |
1 files changed, 1163 insertions, 0 deletions
diff --git a/drivers/multiple/Win32/gdisp_lld_Win32.c b/drivers/multiple/Win32/gdisp_lld_Win32.c new file mode 100644 index 00000000..7d7abb06 --- /dev/null +++ b/drivers/multiple/Win32/gdisp_lld_Win32.c @@ -0,0 +1,1163 @@ +/* + * 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 + */ + +/** + * @file drivers/multiple/Win32/gdisp_lld.c + * @brief GDISP Graphics Driver subsystem low level driver source for Win32. + */ +#include "gfx.h" + +#if GFX_USE_GDISP + +#define GDISP_DRIVER_VMT GDISPVMT_Win32 +#include "../drivers/multiple/Win32/gdisp_lld_config.h" +#include "gdisp/lld/gdisp_lld.h" + +#ifndef GDISP_SCREEN_WIDTH + #define GDISP_SCREEN_WIDTH 640 +#endif +#ifndef GDISP_SCREEN_HEIGHT + #define GDISP_SCREEN_HEIGHT 480 +#endif +#if GDISP_PIXELFORMAT != GDISP_PIXELFORMAT_RGB888 + #error "GDISP Win32: This driver currently only supports the RGB888 pixel format." +#endif +// Setting this to TRUE delays updating the screen +// to the windows paint routine. Due to the +// drawing lock this does not add as much speed +// as might be expected but it is still faster in +// all tested circumstances and for all operations +// even draw_pixel(). +// This is probably due to drawing operations being +// combined as the update regions are merged. +// The only time you might want to turn this off is +// if you are debugging drawing and want to see each +// pixel as it is set. +#define GDISP_WIN32_USE_INDIRECT_UPDATE TRUE +//#define GDISP_WIN32_USE_INDIRECT_UPDATE FALSE + +// How far extra windows (multiple displays) should be offset from the first. +#define DISPLAY_X_OFFSET 50 +#define DISPLAY_Y_OFFSET 50 + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <windows.h> +#include <wingdi.h> +#include <assert.h> + +#define GDISP_FLG_READY (GDISP_FLG_DRIVER<<0) +#define GDISP_FLG_HASTOGGLE (GDISP_FLG_DRIVER<<1) +#define GDISP_FLG_HASMOUSE (GDISP_FLG_DRIVER<<2) +#if GDISP_HARDWARE_STREAM_WRITE || GDISP_HARDWARE_STREAM_READ + #define GDISP_FLG_WSTREAM (GDISP_FLG_DRIVER<<3) + #define GDISP_FLG_WRAPPED (GDISP_FLG_DRIVER<<4) +#endif + +#if GINPUT_NEED_TOGGLE + /* Include toggle support code */ + #include "ginput/lld/toggle.h" +#endif + +#if GINPUT_NEED_MOUSE + /* Include mouse support code */ + #include "ginput/lld/mouse.h" +#endif + +static DWORD winThreadId; +static ATOM winClass; +static volatile bool_t QReady; +static HANDLE drawMutex; +#if GINPUT_NEED_MOUSE + static GDisplay * mouseDisplay; +#endif + +/*===========================================================================*/ +/* Driver local routines . */ +/*===========================================================================*/ + +#if GINPUT_NEED_TOGGLE + #define WIN32_BUTTON_AREA 16 +#else + #define WIN32_BUTTON_AREA 0 +#endif + +#define APP_NAME "uGFX" + +#define COLOR2BGR(c) ((((c) & 0xFF)<<16)|((c) & 0xFF00)|(((c)>>16) & 0xFF)) +#define BGR2COLOR(c) COLOR2BGR(c) + +typedef struct winPriv { + HWND hwnd; + HDC dcBuffer; + HBITMAP dcBitmap; + HBITMAP dcOldBitmap; + #if GINPUT_NEED_MOUSE + coord_t mousex, mousey; + uint16_t mousebuttons; + #endif + #if GINPUT_NEED_TOGGLE + uint8_t toggles; + #endif + #if GDISP_HARDWARE_STREAM_WRITE || GDISP_HARDWARE_STREAM_READ + coord_t x0, y0, x1, y1; + coord_t x, y; + #endif +} winPriv; + + +static LRESULT myWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + HDC dc; + PAINTSTRUCT ps; + GDisplay * g; + winPriv * priv; + #if GINPUT_NEED_TOGGLE + HBRUSH hbrOn, hbrOff; + HPEN pen; + RECT rect; + HGDIOBJ old; + POINT p; + coord_t pos; + uint8_t bit; + #endif + + switch (Msg) { + case WM_CREATE: + // Get our GDisplay structure and attach it to the window + g = (GDisplay *)((LPCREATESTRUCT)lParam)->lpCreateParams; + priv = (winPriv *)g->priv; + SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)g); + + // Fill in the private area + priv->hwnd = hWnd; + dc = GetDC(hWnd); + priv->dcBitmap = CreateCompatibleBitmap(dc, g->g.Width, g->g.Height); + priv->dcBuffer = CreateCompatibleDC(dc); + ReleaseDC(hWnd, dc); + priv->dcOldBitmap = SelectObject(priv->dcBuffer, priv->dcBitmap); + + // Mark the window as ready to go + g->flags |= GDISP_FLG_READY; + break; + + #if GINPUT_NEED_MOUSE || GINPUT_NEED_TOGGLE + case WM_LBUTTONDOWN: + // Get our GDisplay structure + g = (GDisplay *)GetWindowLongPtr(hWnd, GWLP_USERDATA); + priv = (winPriv *)g->priv; + + // Handle mouse down on the window + #if GINPUT_NEED_MOUSE + if ((coord_t)HIWORD(lParam) < GDISP_SCREEN_HEIGHT && (g->flags & GDISP_FLG_HASMOUSE)) { + priv->mousebuttons |= GINPUT_MOUSE_BTN_LEFT; + goto mousemove; + } + #endif + + // Handle mouse down on the toggle area + #if GINPUT_NEED_TOGGLE + if ((coord_t)HIWORD(lParam) >= GDISP_SCREEN_HEIGHT && (g->flags & GDISP_FLG_HASTOGGLE)) { + bit = 1 << ((coord_t)LOWORD(lParam)*8/g->g.Width); + priv->toggles ^= bit; + rect.left = 0; + rect.right = GDISP_SCREEN_WIDTH; + rect.top = GDISP_SCREEN_HEIGHT; + rect.bottom = GDISP_SCREEN_HEIGHT + WIN32_BUTTON_AREA; + InvalidateRect(hWnd, &rect, FALSE); + UpdateWindow(hWnd); + #if GINPUT_TOGGLE_POLL_PERIOD == TIME_INFINITE + ginputToggleWakeup(); + #endif + } + #endif + break; + + case WM_LBUTTONUP: + // Get our GDisplay structure + g = (GDisplay *)GetWindowLongPtr(hWnd, GWLP_USERDATA); + priv = (winPriv *)g->priv; + + // Handle mouse up on the toggle area + #if GINPUT_NEED_TOGGLE + if ((g->flags & GDISP_FLG_HASTOGGLE)) { + if ((priv->toggles & 0x0F)) { + priv->toggles &= ~0x0F; + rect.left = 0; + rect.right = GDISP_SCREEN_WIDTH; + rect.top = GDISP_SCREEN_HEIGHT; + rect.bottom = GDISP_SCREEN_HEIGHT + WIN32_BUTTON_AREA; + InvalidateRect(hWnd, &rect, FALSE); + UpdateWindow(hWnd); + #if GINPUT_TOGGLE_POLL_PERIOD == TIME_INFINITE + ginputToggleWakeup(); + #endif + } + } + #endif + + // Handle mouse up on the window + #if GINPUT_NEED_MOUSE + if ((coord_t)HIWORD(lParam) < GDISP_SCREEN_HEIGHT && (g->flags & GDISP_FLG_HASMOUSE)) { + priv->mousebuttons &= ~GINPUT_MOUSE_BTN_LEFT; + goto mousemove; + } + #endif + break; + #endif + + #if GINPUT_NEED_MOUSE + case WM_MBUTTONDOWN: + g = (GDisplay *)GetWindowLongPtr(hWnd, GWLP_USERDATA); + priv = (winPriv *)g->priv; + if ((coord_t)HIWORD(lParam) < GDISP_SCREEN_HEIGHT && (g->flags & GDISP_FLG_HASMOUSE)) { + priv->mousebuttons |= GINPUT_MOUSE_BTN_MIDDLE; + goto mousemove; + } + break; + case WM_MBUTTONUP: + g = (GDisplay *)GetWindowLongPtr(hWnd, GWLP_USERDATA); + priv = (winPriv *)g->priv; + if ((coord_t)HIWORD(lParam) < GDISP_SCREEN_HEIGHT && (g->flags & GDISP_FLG_HASMOUSE)) { + priv->mousebuttons &= ~GINPUT_MOUSE_BTN_MIDDLE; + goto mousemove; + } + break; + case WM_RBUTTONDOWN: + g = (GDisplay *)GetWindowLongPtr(hWnd, GWLP_USERDATA); + priv = (winPriv *)g->priv; + if ((coord_t)HIWORD(lParam) < GDISP_SCREEN_HEIGHT && (g->flags & GDISP_FLG_HASMOUSE)) { + priv->mousebuttons |= GINPUT_MOUSE_BTN_RIGHT; + goto mousemove; + } + break; + case WM_RBUTTONUP: + g = (GDisplay *)GetWindowLongPtr(hWnd, GWLP_USERDATA); + priv = (winPriv *)g->priv; + if ((coord_t)HIWORD(lParam) < GDISP_SCREEN_HEIGHT && (g->flags & GDISP_FLG_HASMOUSE)) { + priv->mousebuttons &= ~GINPUT_MOUSE_BTN_RIGHT; + goto mousemove; + } + break; + case WM_MOUSEMOVE: + g = (GDisplay *)GetWindowLongPtr(hWnd, GWLP_USERDATA); + priv = (winPriv *)g->priv; + if ((coord_t)HIWORD(lParam) >= GDISP_SCREEN_HEIGHT || !(g->flags & GDISP_FLG_HASMOUSE)) + break; + mousemove: + priv->mousex = (coord_t)LOWORD(lParam); + priv->mousey = (coord_t)HIWORD(lParam); + #if GINPUT_MOUSE_POLL_PERIOD == TIME_INFINITE + ginputMouseWakeup(); + #endif + break; + #endif + + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + case WM_SYSKEYUP: + case WM_KEYUP: + break; + case WM_CHAR: + case WM_DEADCHAR: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + break; + + case WM_ERASEBKGND: + // Pretend we have erased the background. + // We know we don't really need to do this as we + // redraw the entire surface in the WM_PAINT handler. + return TRUE; + + case WM_PAINT: + // Get our GDisplay structure + g = (GDisplay *)GetWindowLongPtr(hWnd, GWLP_USERDATA); + priv = (winPriv *)g->priv; + + // Paint the main window area + WaitForSingleObject(drawMutex, INFINITE); + dc = BeginPaint(hWnd, &ps); + BitBlt(dc, ps.rcPaint.left, ps.rcPaint.top, + ps.rcPaint.right - ps.rcPaint.left, + (ps.rcPaint.bottom > GDISP_SCREEN_HEIGHT ? GDISP_SCREEN_HEIGHT : ps.rcPaint.bottom) - ps.rcPaint.top, + priv->dcBuffer, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY); + + // Paint the toggle area + #if GINPUT_NEED_TOGGLE + if (ps.rcPaint.bottom >= GDISP_SCREEN_HEIGHT && (g->flags & GDISP_FLG_HASTOGGLE)) { + pen = CreatePen(PS_SOLID, 1, COLOR2BGR(Black)); + hbrOn = CreateSolidBrush(COLOR2BGR(Blue)); + hbrOff = CreateSolidBrush(COLOR2BGR(Gray)); + old = SelectObject(dc, pen); + MoveToEx(dc, 0, GDISP_SCREEN_HEIGHT, &p); + LineTo(dc, GDISP_SCREEN_WIDTH, GDISP_SCREEN_HEIGHT); + for(pos = 0, bit=1; pos < wWidth; pos=rect.right, bit <<= 1) { + rect.left = pos; + rect.right = pos + GDISP_SCREEN_WIDTH/8; + rect.top = GDISP_SCREEN_HEIGHT; + rect.bottom = GDISP_SCREEN_HEIGHT + WIN32_BUTTON_AREA; + FillRect(dc, &rect, (priv->toggles & bit) ? hbrOn : hbrOff); + if (pos > 0) { + MoveToEx(dc, rect.left, rect.top, &p); + LineTo(dc, rect.left, rect.bottom); + } + } + DeleteObject(hbrOn); + DeleteObject(hbrOff); + SelectObject(dc, old); + } + #endif + EndPaint(hWnd, &ps); + ReleaseMutex(drawMutex); + break; + + case WM_DESTROY: + // Get our GDisplay structure + g = (GDisplay *)GetWindowLongPtr(hWnd, GWLP_USERDATA); + priv = (winPriv *)g->priv; + + // Restore the window and free our bitmaps + SelectObject(priv->dcBuffer, priv->dcOldBitmap); + DeleteDC(priv->dcBuffer); + DeleteObject(priv->dcBitmap); + + // Cleanup the private area + gfxFree(priv); + + // Quit the application + PostQuitMessage(0); + + // Actually the above doesn't work (who knows why) + ExitProcess(0); + break; + + default: + return DefWindowProc(hWnd, Msg, wParam, lParam); + } + return 0; +} + +static DECLARE_THREAD_STACK(waWindowThread, 1024); +static DECLARE_THREAD_FUNCTION(WindowThread, param) { + (void)param; + MSG msg; + + // Establish this thread as a message queue thread + winThreadId = GetCurrentThreadId(); + PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + QReady = TRUE; + + do { + gfxSleepMilliseconds(1); + while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + // Is this our special thread message to create a new window? + if (!msg.hwnd && msg.message == WM_USER) { + RECT rect; + GDisplay *g; + + g = (GDisplay *)msg.lParam; + + // Set the window rectangle + rect.top = 0; rect.bottom = g->g.Height; + rect.left = 0; rect.right = g->g.Width; + #if GINPUT_NEED_TOGGLE + if ((g->flags & GDISP_FLG_HASTOGGLE)) + rect.bottom += WIN32_BUTTON_AREA; + #endif + AdjustWindowRect(&rect, WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU, 0); + + // Create the window + msg.hwnd = CreateWindow(APP_NAME, "", WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_BORDER, msg.wParam*DISPLAY_X_OFFSET, msg.wParam*DISPLAY_Y_OFFSET, + rect.right-rect.left, rect.bottom-rect.top, 0, 0, + GetModuleHandle(NULL), g); + assert(msg.hwnd != NULL); + + // Or just a normal window message + } else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } while (msg.message != WM_QUIT); + ExitProcess(0); + return msg.wParam; +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { + winPriv * priv; + char buf[132]; + + // Initialise the window thread and the window class (if it hasn't been done already) + if (!QReady) { + gfxThreadHandle hth; + WNDCLASS wc; + + // Create the draw mutex + drawMutex = CreateMutex(NULL, FALSE, NULL); + + // Create the thread + hth = gfxThreadCreate(waWindowThread, sizeof(waWindowThread), HIGH_PRIORITY, WindowThread, 0); + assert(hth != NULL); + gfxThreadClose(hth); + + wc.style = CS_HREDRAW | CS_VREDRAW; // | CS_OWNDC; + wc.lpfnWndProc = (WNDPROC)myWindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = GetModuleHandle(NULL); + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = GetStockObject(WHITE_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = APP_NAME; + winClass = RegisterClass(&wc); + assert(winClass != 0); + + // Wait for our thread to be ready + while (!QReady) + Sleep(1); + } + + // Initialise the GDISP structure + g->g.Orientation = GDISP_ROTATE_0; + g->g.Powermode = powerOn; + g->g.Backlight = 100; + g->g.Contrast = 50; + g->g.Width = GDISP_SCREEN_WIDTH; + g->g.Height = GDISP_SCREEN_HEIGHT; + + // Turn on toggles for the first GINPUT_TOGGLE_CONFIG_ENTRIES windows + #if GINPUT_NEED_TOGGLE + if (g->controllerdisplay < GINPUT_TOGGLE_CONFIG_ENTRIES) + g->flags |= GDISP_FLG_HASTOGGLE; + #endif + + // Only turn on mouse on the first window for now + #if GINPUT_NEED_MOUSE + if (!g->controllerdisplay) { + mouseDisplay = g; + g->flags |= GDISP_FLG_HASMOUSE; + } + #endif + + // Create a private area for this window + priv = (winPriv *)gfxAlloc(sizeof(winPriv)); + assert(priv != NULL); + memset(priv, 0, sizeof(winPriv)); + g->priv = priv; + #if GDISP_HARDWARE_STREAM_WRITE || GDISP_HARDWARE_STREAM_READ + // Initialise with an invalid window + g->flags &= ~GDISP_FLG_WSTREAM; + #endif + g->board = 0; // no board interface for this controller + + // Create the window in the message thread + PostThreadMessage(winThreadId, WM_USER, (WPARAM)g->controllerdisplay, (LPARAM)g); + + // Wait for the window creation to complete (for safety) + while(!(((volatile GDisplay *)g)->flags & GDISP_FLG_READY)) + Sleep(1); + + sprintf(buf, APP_NAME " - %u", g->systemdisplay+1); + SetWindowText(priv->hwnd, buf); + ShowWindow(priv->hwnd, SW_SHOW); + UpdateWindow(priv->hwnd); + + return TRUE; +} + +#if GDISP_HARDWARE_FLUSH + LLDSPEC void gdisp_lld_flush(GDisplay *g) { + winPriv * priv; + + priv = g->priv; + UpdateWindow(priv->hwnd); + } +#endif + +#if GDISP_HARDWARE_STREAM_WRITE || GDISP_HARDWARE_STREAM_READ + void BAD_PARAMETER(const char *msg) { + volatile int a; + // This is really just a point for us to set the debugger + a = 0; + } +#endif + +#if GDISP_HARDWARE_STREAM_WRITE + LLDSPEC void gdisp_lld_write_start(GDisplay *g) { + winPriv * priv; + + if (g->flags & GDISP_FLG_WSTREAM) + BAD_PARAMETER("write_start: already in streaming mode"); + if (g->p.cx <= 0 || g->p.cy <= 0 || g->p.x < 0 || g->p.y < 0 || g->p.x+g->p.cx > g->g.Width || g->p.y+g->p.cy > g->g.Height) + BAD_PARAMETER("write_start: bad window parameter"); + + priv = g->priv; + priv->x0 = g->p.x; priv->x1 = g->p.x + g->p.cx - 1; + priv->y0 = g->p.y; priv->y1 = g->p.y + g->p.cy - 1; + #if GDISP_HARDWARE_STREAM_POS + priv->x = g->p.x-1; // Make sure these values are invalid (for testing) + priv->y = g->p.y-1; + #else + priv->x = g->p.x; + priv->y = g->p.y; + #endif + g->flags |= GDISP_FLG_WSTREAM; + g->flags &= ~GDISP_FLG_WRAPPED; + } + LLDSPEC void gdisp_lld_write_color(GDisplay *g) { + winPriv * priv; + int x, y; + COLORREF color; + + priv = g->priv; + color = COLOR2BGR(g->p.color); + + if (!(g->flags & GDISP_FLG_WSTREAM)) + BAD_PARAMETER("write_color: not in streaming mode"); + if (priv->x < priv->x0 || priv->x > priv->x1 || priv->y < priv->y0 || priv->y > priv->y1) + BAD_PARAMETER("write_color: cursor outside streaming area"); + if (g->flags & GDISP_FLG_WRAPPED) { + BAD_PARAMETER("write_color: Warning - Area wrapped."); + g->flags &= ~GDISP_FLG_WRAPPED; + } + + #if GDISP_NEED_CONTROL + switch(g->g.Orientation) { + case GDISP_ROTATE_0: + x = priv->x; + y = priv->y; + break; + case GDISP_ROTATE_90: + x = priv->y; + y = g->g.Width - 1 - priv->x; + break; + case GDISP_ROTATE_180: + x = g->g.Width - 1 - priv->x; + y = g->g.Height - 1 - priv->y; + break; + case GDISP_ROTATE_270: + x = g->g.Height - 1 - priv->y; + y = priv->x; + break; + } + #else + x = priv->x; + y = priv->y; + #endif + + // Draw the pixel on the screen and in the buffer. + WaitForSingleObject(drawMutex, INFINITE); + SetPixel(priv->dcBuffer, x, y, color); + #if GDISP_WIN32_USE_INDIRECT_UPDATE + ReleaseMutex(drawMutex); + { + RECT r; + r.left = x; r.right = x+1; + r.top = y; r.bottom = y+1; + InvalidateRect(priv->hwnd, &r, FALSE); + } + #else + { + HDC dc; + dc = GetDC(priv->hwnd); + SetPixel(dc, x, y, color); + ReleaseDC(priv->hwnd, dc); + ReleaseMutex(drawMutex); + } + #endif + + // Update the cursor + if (++priv->x > priv->x1) { + priv->x = priv->x0; + if (++priv->y > priv->y1) { + g->flags |= GDISP_FLG_WRAPPED; + priv->y = priv->y0; + } + } + } + LLDSPEC void gdisp_lld_write_stop(GDisplay *g) { + if (!(g->flags & GDISP_FLG_WSTREAM)) + BAD_PARAMETER("write_stop: not in streaming mode"); + g->flags &= ~GDISP_FLG_WSTREAM; + } + #if GDISP_HARDWARE_STREAM_POS + LLDSPEC void gdisp_lld_write_pos(GDisplay *g) { + winPriv * priv; + + priv = g->priv; + + if (!(g->flags & GDISP_FLG_WSTREAM)) + BAD_PARAMETER("write_pos: not in streaming mode"); + if (g->p.x < priv->x0 || g->p.x > priv->x1 || g->p.y < priv->y0 || g->p.y > priv->y1) + BAD_PARAMETER("write_color: new cursor outside streaming area"); + priv->x = g->p.x; + priv->y = g->p.y; + } + #endif +#endif + +#if GDISP_HARDWARE_STREAM_READ + LLDSPEC void gdisp_lld_read_start(GDisplay *g) { + winPriv * priv; + + if (g->flags & GDISP_FLG_WSTREAM) + BAD_PARAMETER("read_start: already in streaming mode"); + if (g->p.cx <= 0 || g->p.cy <= 0 || g->p.x < 0 || g->p.y < 0 || g->p.x+g->p.cx > g->g.Width || g->p.y+g->p.cy > g->g.Height) + BAD_PARAMETER("read_start: bad window parameter"); + + priv = g->priv; + priv->x0 = g->p.x; priv->x1 = g->p.x + g->p.cx - 1; + priv->y0 = g->p.y; priv->y1 = g->p.y + g->p.cy - 1; + priv->x = g->p.x; + priv->y = g->p.y; + g->flags |= GDISP_FLG_WSTREAM; + g->flags &= ~GDISP_FLG_WRAPPED; + } + LLDSPEC color_t gdisp_lld_read_color(GDisplay *g) { + winPriv * priv; + COLORREF color; + + priv = g->priv; + + if (!(g->flags & GDISP_FLG_WSTREAM)) + BAD_PARAMETER("read_color: not in streaming mode"); + if (priv->x < priv->x0 || priv->x > priv->x1 || priv->y < priv->y0 || priv->y > priv->y1) + BAD_PARAMETER("read_color: cursor outside streaming area"); + if (g->flags & GDISP_FLG_WRAPPED) { + BAD_PARAMETER("read_color: Warning - Area wrapped."); + g->flags &= ~GDISP_FLG_WRAPPED; + } + + WaitForSingleObject(drawMutex, INFINITE); + #if GDISP_NEED_CONTROL + switch(g->g.Orientation) { + case GDISP_ROTATE_0: + color = GetPixel(priv->dcBuffer, g->p.x, g->p.y); + break; + case GDISP_ROTATE_90: + color = GetPixel(priv->dcBuffer, g->p.y, g->g.Width - 1 - g->p.x); + break; + case GDISP_ROTATE_180: + color = GetPixel(priv->dcBuffer, g->g.Width - 1 - g->p.x, g->g.Height - 1 - g->p.y); + break; + case GDISP_ROTATE_270: + color = GetPixel(priv->dcBuffer, g->g.Height - 1 - g->p.y, g->p.x); + break; + } + #else + color = GetPixel(priv->dcBuffer, g->p.x, g->p.y); + #endif + ReleaseMutex(drawMutex); + + // Update the cursor + if (++priv->x > priv->x1) { + priv->x = priv->x0; + if (++priv->y > priv->y1) { + g->flags |= GDISP_FLG_WRAPPED; + priv->y = priv->y0; + } + } + + return BGR2COLOR(color); + } + LLDSPEC void gdisp_lld_read_stop(GDisplay *g) { + if (!(g->flags & GDISP_FLG_WSTREAM)) + BAD_PARAMETER("write_stop: not in streaming mode"); + g->flags &= ~GDISP_FLG_WSTREAM; + } +#endif + +#if GDISP_HARDWARE_DRAWPIXEL + LLDSPEC void gdisp_lld_draw_pixel(GDisplay *g) { + winPriv * priv; + int x, y; + COLORREF color; + + priv = g->priv; + color = COLOR2BGR(g->p.color); + + #if GDISP_NEED_CONTROL + switch(g->g.Orientation) { + case GDISP_ROTATE_0: + x = g->p.x; + y = g->p.y; + break; + case GDISP_ROTATE_90: + x = g->p.y; + y = g->g.Width - 1 - g->p.x; + break; + case GDISP_ROTATE_180: + x = g->g.Width - 1 - g->p.x; + y = g->g.Height - 1 - g->p.y; + break; + case GDISP_ROTATE_270: + x = g->g.Height - 1 - g->p.y; + y = g->p.x; + break; + } + #else + x = g->p.x; + y = g->p.y; + #endif + + // Draw the pixel on the screen and in the buffer. + WaitForSingleObject(drawMutex, INFINITE); + SetPixel(priv->dcBuffer, x, y, color); + #if GDISP_WIN32_USE_INDIRECT_UPDATE + ReleaseMutex(drawMutex); + { + RECT r; + r.left = x; r.right = x+1; + r.top = y; r.bottom = y+1; + InvalidateRect(priv->hwnd, &r, FALSE); + } + #else + { + HDC dc; + dc = GetDC(priv->hwnd); + SetPixel(dc, x, y, color); + ReleaseDC(priv->hwnd, dc); + ReleaseMutex(drawMutex); + } + #endif + } +#endif + +/* ---- Optional Routines ---- */ + +#if GDISP_HARDWARE_FILLS + LLDSPEC void gdisp_lld_fill_area(GDisplay *g) { + winPriv * priv; + RECT rect; + HBRUSH hbr; + COLORREF color; + + priv = g->priv; + color = COLOR2BGR(g->p.color); + hbr = CreateSolidBrush(color); + + #if GDISP_NEED_CONTROL + switch(g->g.Orientation) { + case GDISP_ROTATE_0: + rect.top = g->p.y; + rect.bottom = rect.top + g->p.cy; + rect.left = g->p.x; + rect.right = rect.left + g->p.cx; + break; + case GDISP_ROTATE_90: + rect.bottom = g->g.Width - g->p.x; + rect.top = rect.bottom - g->p.cx; + rect.left = g->p.y; + rect.right = rect.left + g->p.cy; + break; + case GDISP_ROTATE_180: + rect.bottom = g->g.Height - g->p.y; + rect.top = rect.bottom - g->p.cy; + rect.right = g->g.Width - g->p.x; + rect.left = rect.right - g->p.cx; + break; + case GDISP_ROTATE_270: + rect.top = g->p.x; + rect.bottom = rect.top + g->p.cx; + rect.right = g->g.Height - g->p.y; + rect.left = rect.right - g->p.cy; + break; + } + #else + rect.top = g->p.y; + rect.bottom = rect.top + g->p.cy; + rect.left = g->p.x; + rect.right = rect.left + g->p.cx; + #endif + + + WaitForSingleObject(drawMutex, INFINITE); + FillRect(priv->dcBuffer, &rect, hbr); + #if GDISP_WIN32_USE_INDIRECT_UPDATE + ReleaseMutex(drawMutex); + InvalidateRect(priv->hwnd, &rect, FALSE); + #else + { + HDC dc; + dc = GetDC(priv->hwnd); + FillRect(dc, &rect, hbr); + ReleaseDC(priv->hwnd, dc); + ReleaseMutex(drawMutex); + } + #endif + + DeleteObject(hbr); + } +#endif + +#if GDISP_HARDWARE_BITFILLS && GDISP_NEED_CONTROL + static pixel_t *rotateimg(GDisplay *g, const pixel_t *buffer) { + pixel_t *dstbuf; + pixel_t *dst; + const pixel_t *src; + size_t sz; + coord_t i, j; + + // Allocate the destination buffer + sz = (size_t)g->p.cx * (size_t)g->p.cy; + if (!(dstbuf = (pixel_t *)malloc(sz * sizeof(pixel_t)))) + return 0; + + // Copy the bits we need + switch(g->g.Orientation) { + case GDISP_ROTATE_0: + return 0; // not handled as it doesn't need to be. + case GDISP_ROTATE_90: + for(src = buffer+g->p.x1, j = 0; j < g->p.cy; j++, src += g->p.x2 - g->p.cx) { + dst = dstbuf+sz-g->p.cy+j; + for(i = 0; i < g->p.cx; i++, dst -= g->p.cy) + *dst = *src++; + } + break; + case GDISP_ROTATE_180: + for(dst = dstbuf+sz, src = buffer+g->p.x1, j = 0; j < g->p.cy; j++, src += g->p.x2 - g->p.cx) + for(i = 0; i < g->p.cx; i++) + *--dst = *src++; + break; + case GDISP_ROTATE_270: + for(src = buffer+g->p.x1, j = 0; j < g->p.cy; j++, src += g->p.x2 - g->p.cx) { + dst = dstbuf+g->p.cy-j-1; + for(i = 0; i < g->p.cx; i++, dst += g->p.cy) + *dst = *src++; + } + break; + } + return dstbuf; + } +#endif + +#if GDISP_HARDWARE_BITFILLS + LLDSPEC void gdisp_lld_blit_area(GDisplay *g) { + winPriv * priv; + pixel_t * buffer; + RECT rect; + BITMAPV4HEADER bmpInfo; + + // Make everything relative to the start of the line + priv = g->priv; + buffer = g->p.ptr; + buffer += g->p.x2*g->p.y1; + + memset(&bmpInfo, 0, sizeof(bmpInfo)); + bmpInfo.bV4Size = sizeof(bmpInfo); + bmpInfo.bV4Planes = 1; + bmpInfo.bV4BitCount = sizeof(pixel_t)*8; + bmpInfo.bV4AlphaMask = 0; + bmpInfo.bV4RedMask = RGB2COLOR(255,0,0); + bmpInfo.bV4GreenMask = RGB2COLOR(0,255,0); + bmpInfo.bV4BlueMask = RGB2COLOR(0,0,255); + bmpInfo.bV4V4Compression = BI_BITFIELDS; + bmpInfo.bV4XPelsPerMeter = 3078; + bmpInfo.bV4YPelsPerMeter = 3078; + bmpInfo.bV4ClrUsed = 0; + bmpInfo.bV4ClrImportant = 0; + bmpInfo.bV4CSType = 0; //LCS_sRGB; + + #if GDISP_NEED_CONTROL + switch(g->g.Orientation) { + case GDISP_ROTATE_0: + bmpInfo.bV4SizeImage = (g->p.cy*g->p.x2) * sizeof(pixel_t); + bmpInfo.bV4Width = g->p.x2; + bmpInfo.bV4Height = -g->p.cy; /* top-down image */ + rect.top = g->p.y; + rect.bottom = rect.top+g->p.cy; + rect.left = g->p.x; + rect.right = rect.left+g->p.cx; + break; + case GDISP_ROTATE_90: + if (!(buffer = rotateimg(g, buffer))) return; + bmpInfo.bV4SizeImage = (g->p.cy*g->p.cx) * sizeof(pixel_t); + bmpInfo.bV4Width = g->p.cy; + bmpInfo.bV4Height = -g->p.cx; /* top-down image */ + rect.bottom = g->g.Width - g->p.x; + rect.top = rect.bottom-g->p.cx; + rect.left = g->p.y; + rect.right = rect.left+g->p.cy; + break; + case GDISP_ROTATE_180: + if (!(buffer = rotateimg(g, buffer))) return; + bmpInfo.bV4SizeImage = (g->p.cy*g->p.cx) * sizeof(pixel_t); + bmpInfo.bV4Width = g->p.cx; + bmpInfo.bV4Height = -g->p.cy; /* top-down image */ + rect.bottom = g->g.Height-1 - g->p.y; + rect.top = rect.bottom-g->p.cy; + rect.right = g->g.Width - g->p.x; + rect.left = rect.right-g->p.cx; + break; + case GDISP_ROTATE_270: + if (!(buffer = rotateimg(g, buffer))) return; + bmpInfo.bV4SizeImage = (g->p.cy*g->p.cx) * sizeof(pixel_t); + bmpInfo.bV4Width = g->p.cy; + bmpInfo.bV4Height = -g->p.cx; /* top-down image */ + rect.top = g->p.x; + rect.bottom = rect.top+g->p.cx; + rect.right = g->g.Height - g->p.y; + rect.left = rect.right-g->p.cy; + break; + } + #else + bmpInfo.bV4SizeImage = (g->p.cy*g->p.x2) * sizeof(pixel_t); + bmpInfo.bV4Width = g->p.x2; + bmpInfo.bV4Height = -g->p.cy; /* top-down image */ + rect.top = g->p.y; + rect.bottom = rect.top+g->p.cy; + rect.left = g->p.x; + rect.right = rect.left+g->p.cx; + #endif + + WaitForSingleObject(drawMutex, INFINITE); + SetDIBitsToDevice(priv->dcBuffer, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0, 0, 0, rect.bottom-rect.top, buffer, (BITMAPINFO*)&bmpInfo, DIB_RGB_COLORS); + #if GDISP_WIN32_USE_INDIRECT_UPDATE + ReleaseMutex(drawMutex); + InvalidateRect(priv->hwnd, &rect, FALSE); + #else + { + HDC dc; + dc = GetDC(priv->hwnd); + SetDIBitsToDevice(dc, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0, 0, 0, rect.bottom-rect.top, buffer, (BITMAPINFO*)&bmpInfo, DIB_RGB_COLORS); + ReleaseDC(priv->hwnd, dc); + ReleaseMutex(drawMutex); + } + #endif + + #if GDISP_NEED_CONTROL + if (buffer != (pixel_t *)g->p.ptr) + free(buffer); + #endif + } +#endif + +#if GDISP_HARDWARE_PIXELREAD + LLDSPEC color_t gdisp_lld_get_pixel_color(GDisplay *g) { + winPriv * priv; + COLORREF color; + + priv = g->priv; + + WaitForSingleObject(drawMutex, INFINITE); + #if GDISP_NEED_CONTROL + switch(g->g.Orientation) { + case GDISP_ROTATE_0: + color = GetPixel(priv->dcBuffer, g->p.x, g->p.y); + break; + case GDISP_ROTATE_90: + color = GetPixel(priv->dcBuffer, g->p.y, g->g.Width - 1 - g->p.x); + break; + case GDISP_ROTATE_180: + color = GetPixel(priv->dcBuffer, g->g.Width - 1 - g->p.x, g->g.Height - 1 - g->p.y); + break; + case GDISP_ROTATE_270: + color = GetPixel(priv->dcBuffer, g->g.Height - 1 - g->p.y, g->p.x); + break; + } + #else + color = GetPixel(priv->dcBuffer, g->p.x, g->p.y); + #endif + ReleaseMutex(drawMutex); + + return BGR2COLOR(color); + } +#endif + +#if GDISP_NEED_SCROLL && GDISP_HARDWARE_SCROLL + LLDSPEC void gdisp_lld_vertical_scroll(GDisplay *g) { + winPriv * priv; + RECT rect; + coord_t lines; + + priv = g->priv; + + #if GDISP_NEED_CONTROL + switch(g->g.Orientation) { + case GDISP_ROTATE_0: + rect.top = g->p.y; + rect.bottom = rect.top+g->p.cy; + rect.left = g->p.x; + rect.right = rect.left+g->p.cx; + lines = -g->p.y1; + goto vertical_scroll; + case GDISP_ROTATE_90: + rect.bottom = g->g.Width - g->p.x; + rect.top = rect.bottom-g->p.cx; + rect.left = g->p.y; + rect.right = rect.left+g->p.cy; + lines = -g->p.y1; + goto horizontal_scroll; + case GDISP_ROTATE_180: + rect.bottom = g->g.Height - g->p.y; + rect.top = rect.bottom-g->p.cy; + rect.right = g->g.Width - g->p.x; + rect.left = rect.right-g->p.cx; + lines = g->p.y1; + vertical_scroll: + if (lines > 0) { + rect.bottom -= lines; + } else { + rect.top -= lines; + } + if (g->p.cy >= lines && g->p.cy >= -lines) { + WaitForSingleObject(drawMutex, INFINITE); + ScrollDC(priv->dcBuffer, 0, lines, &rect, 0, 0, 0); + #if GDISP_WIN32_USE_INDIRECT_UPDATE + ReleaseMutex(drawMutex); + InvalidateRect(priv->hwnd, &rect, FALSE); + #else + { + HDC dc; + dc = GetDC(priv->hwnd); + ScrollDC(dc, 0, lines, &rect, 0, 0, 0); + ReleaseDC(priv->hwnd, dc); + ReleaseMutex(drawMutex); + } + #endif + } + break; + case GDISP_ROTATE_270: + rect.top = g->p.x; + rect.bottom = rect.top+g->p.cx; + rect.right = g->g.Height - g->p.y; + rect.left = rect.right-g->p.cy; + lines = g->p.y1; + horizontal_scroll: + if (lines > 0) { + rect.right -= lines; + } else { + rect.left -= lines; + } + if (g->p.cy >= lines && g->p.cy >= -lines) { + WaitForSingleObject(drawMutex, INFINITE); + ScrollDC(priv->dcBuffer, lines, 0, &rect, 0, 0, 0); + #if GDISP_WIN32_USE_INDIRECT_UPDATE + ReleaseMutex(drawMutex); + InvalidateRect(priv->hwnd, &rect, FALSE); + #else + { + HDC dc; + dc = GetDC(priv->hwnd); + ScrollDC(dc, lines, 0, &rect, 0, 0, 0); + ReleaseDC(priv->hwnd, dc); + ReleaseMutex(drawMutex); + } + #endif + } + break; + } + #else + rect.top = g->p.y; + rect.bottom = rect.top+g->p.cy; + rect.left = g->p.x; + rect.right = rect.left+g->p.cx; + lines = -g->p.y1; + if (lines > 0) { + rect.bottom -= lines; + } else { + rect.top -= lines; + } + if (g->p.cy >= lines && g->p.cy >= -lines) { + WaitForSingleObject(drawMutex, INFINITE); + ScrollDC(priv->dcBuffer, 0, lines, &rect, 0, 0, 0); + #if GDISP_WIN32_USE_INDIRECT_UPDATE + ReleaseMutex(drawMutex); + InvalidateRect(priv->hwnd, &rect, FALSE); + #else + { + HDC dc; + dc = GetDC(priv->hwnd); + ScrollDC(dc, 0, lines, &rect, 0, 0, 0); + ReleaseDC(priv->hwnd, dc); + ReleaseMutex(drawMutex); + } + #endif + } + #endif + } +#endif + +#if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL + LLDSPEC void gdisp_lld_control(GDisplay *g) { + switch(g->p.x) { + case GDISP_CONTROL_ORIENTATION: + if (g->g.Orientation == (orientation_t)g->p.ptr) + return; + switch((orientation_t)g->p.ptr) { + case GDISP_ROTATE_0: + case GDISP_ROTATE_180: + g->g.Width = GDISP_SCREEN_WIDTH; + g->g.Height = GDISP_SCREEN_HEIGHT; + break; + case GDISP_ROTATE_90: + case GDISP_ROTATE_270: + g->g.Height = GDISP_SCREEN_WIDTH; + g->g.Width = GDISP_SCREEN_HEIGHT; + break; + default: + return; + } + g->g.Orientation = (orientation_t)g->p.ptr; + return; +/* + case GDISP_CONTROL_POWER: + case GDISP_CONTROL_BACKLIGHT: + case GDISP_CONTROL_CONTRAST: +*/ + } + } +#endif + +#if GINPUT_NEED_MOUSE + void ginput_lld_mouse_init(void) {} + void ginput_lld_mouse_get_reading(MouseReading *pt) { + GDisplay * g; + winPriv * priv; + + g = mouseDisplay; + priv = g->priv; + + pt->x = priv->mousex; + pt->y = priv->mousey > g->g.Height ? g->g.Height : priv->mousey; + pt->z = (priv->mousebuttons & GINPUT_MOUSE_BTN_LEFT) ? 100 : 0; + pt->buttons = priv->mousebuttons; + } +#endif /* GINPUT_NEED_MOUSE */ + +#if GINPUT_NEED_TOGGLE + #if GINPUT_TOGGLE_CONFIG_ENTRIES > GDISP_DRIVER_COUNT_WIN32 + #error "GDISP Win32: GINPUT_TOGGLE_CONFIG_ENTRIES must not be greater than GDISP_DRIVER_COUNT_WIN32" + #endif + + GToggleConfig GInputToggleConfigTable[GINPUT_TOGGLE_CONFIG_ENTRIES]; + + void ginput_lld_toggle_init(const GToggleConfig *ptc) { + // Save the associated window struct + ptc->id = &GDISP_WIN32[ptc - GInputToggleConfigTable]; + + // We have 8 buttons per window. + ptc->mask = 0xFF; + + // No inverse or special mode + ptc->invert = 0x00; + ptc->mode = 0; + } + unsigned ginput_lld_toggle_getbits(const GToggleConfig *ptc) { + return ((GDisplay *)(ptc->id))->priv->toggles; + } +#endif /* GINPUT_NEED_TOGGLE */ + +#endif /* GFX_USE_GDISP */ + |