938 lines
25 KiB
C
938 lines
25 KiB
C
/*
|
|
* 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"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <windows.h>
|
|
#include <wingdi.h>
|
|
#include <assert.h>
|
|
|
|
#ifndef GDISP_SCREEN_WIDTH
|
|
#define GDISP_SCREEN_WIDTH 640
|
|
#endif
|
|
#ifndef GDISP_SCREEN_HEIGHT
|
|
#define GDISP_SCREEN_HEIGHT 480
|
|
#endif
|
|
|
|
#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 GINPUT_NEED_TOGGLE
|
|
/* Include toggle support code */
|
|
#include "ginput/lld/toggle.h"
|
|
#endif
|
|
|
|
#if GDISP_PIXELFORMAT != GDISP_PIXELFORMAT_RGB888
|
|
#error "GDISP Win32: This driver currently only supports the RGB888 pixel format."
|
|
#endif
|
|
|
|
#if GINPUT_NEED_MOUSE
|
|
/* Include mouse support code */
|
|
#include "ginput/lld/mouse.h"
|
|
#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.
|
|
#define GDISP_WIN32_USE_INDIRECT_UPDATE TRUE
|
|
|
|
// How far extra windows should be offset from the first.
|
|
#define DISPLAY_X_OFFSET 50
|
|
#define DISPLAY_Y_OFFSET 50
|
|
|
|
static DWORD winThreadId;
|
|
static ATOM winClass;
|
|
static volatile bool_t QReady;
|
|
static HANDLE drawMutex;
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* 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
|
|
} 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)
|
|
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;
|
|
|
|
// 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_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 = g->p.x; r.right = g->p.x+1;
|
|
r.top = g->p.y; r.bottom = g->p.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_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(srcimg);
|
|
#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(GC->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 = GC->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;
|
|
|
|
g = GDISP_WIN32;
|
|
pt->x = g->priv->mousex;
|
|
pt->y = g->priv->mousey > g->g.Height ? g->g.Height : mousey;
|
|
pt->z = (g->priv->mousebuttons & GINPUT_MOUSE_BTN_LEFT) ? 100 : 0;
|
|
pt->buttons = g->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 */
|
|
|