1689 lines
49 KiB
C
1689 lines
49 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.io/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
|
|
|
|
#define GDISP_DRIVER_VMT GDISPVMT_Win32
|
|
#include "gdisp_lld_config.h"
|
|
#include "../../../src/gdisp/gdisp_driver.h"
|
|
|
|
// Configuration parameters for this driver
|
|
#ifndef GDISP_SCREEN_WIDTH
|
|
#define GDISP_SCREEN_WIDTH 640
|
|
#endif
|
|
#ifndef GDISP_SCREEN_HEIGHT
|
|
#define GDISP_SCREEN_HEIGHT 480
|
|
#endif
|
|
#ifndef GDISP_WIN32_USE_INDIRECT_UPDATE
|
|
/**
|
|
* Setting this to GFXON 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 GFXON
|
|
#endif
|
|
#ifndef GKEYBOARD_WIN32_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 Win32 in uGFX (currently only US English), setting this
|
|
* to GFXON enables the windows 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.
|
|
*/
|
|
#define GKEYBOARD_WIN32_NO_LAYOUT GFXOFF
|
|
#endif
|
|
#ifndef GKEYBOARD_WIN32_DEFAULT_LAYOUT
|
|
#define GKEYBOARD_WIN32_DEFAULT_LAYOUT KeyboardLayout_Win32_US
|
|
#endif
|
|
|
|
// How far extra windows (multiple displays) should be offset from the first.
|
|
#define DISPLAY_X_OFFSET 50
|
|
#define DISPLAY_Y_OFFSET 50
|
|
|
|
// Oops - name clashes with Win32 symbols
|
|
#if GFX_COMPAT_V2 && GFX_COMPAT_OLDCOLORS
|
|
#undef Red
|
|
#undef Green
|
|
#undef Blue
|
|
#endif
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#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)
|
|
#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 GFX_USE_GINPUT && GINPUT_NEED_TOGGLE
|
|
/* Include toggle support code */
|
|
#include "../../../src/ginput/ginput_driver_toggle.h"
|
|
|
|
// Hack until toggle use gdriver.
|
|
static GDisplay *toggleWindow;
|
|
#endif
|
|
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
|
|
// Include mouse support code
|
|
#define GMOUSE_DRIVER_VMT GMOUSEVMT_Win32
|
|
#include "../../../src/ginput/ginput_driver_mouse.h"
|
|
|
|
// Forward definitions
|
|
static gBool Win32MouseInit(GMouse *m, unsigned driverinstance);
|
|
static gBool Win32MouseRead(GMouse *m, GMouseReading *prd);
|
|
|
|
/**
|
|
* This should be: const GMouseVMT const GMOUSE_DRIVER_VMT[1] = {{
|
|
* However, some major compilers complain about the duplicate const specifier even though this is perfectly valid standard C.
|
|
*/
|
|
const GMouseVMT 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
|
|
},
|
|
Win32MouseInit, // init
|
|
0, // deinit
|
|
Win32MouseRead, // get
|
|
0, // calsave
|
|
0 // calload
|
|
}};
|
|
#endif
|
|
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD
|
|
#define GKEYBOARD_DRIVER_VMT GKEYBOARDVMT_Win32
|
|
#include "../../../src/ginput/ginput_driver_keyboard.h"
|
|
|
|
#if !GKEYBOARD_WIN32_NO_LAYOUT
|
|
#if GKEYBOARD_LAYOUT_OFF
|
|
#error "The Win32 keyboard driver is using the layout engine. Please set GKEYBOARD_LAYOUT_OFF=GFXOFF or GKEYBOARD_WIN32_NO_LAYOUT=GFXON."
|
|
#endif
|
|
|
|
#include "../../../src/ginput/ginput_keyboard_microcode.h"
|
|
|
|
// Forward definitions
|
|
extern gU8 GKEYBOARD_WIN32_DEFAULT_LAYOUT[];
|
|
|
|
// 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.
|
|
gU8 KeyboardLayout_Win32_US[] = {
|
|
KMC_HEADERSTART, KMC_HEADER_ID1, KMC_HEADER_ID2, KMC_HEADER_VER_1,
|
|
|
|
// Transient Shifters: SHIFT, CTRL, ALT, WINKEY
|
|
/* 1 */KMC_RECORDSTART, 9, // SHIFT (left & Right)
|
|
KMC_TEST_CODETABLE, 2, VK_SHIFT, VK_LSHIFT,
|
|
KMC_TEST_LASTCODE, 0x00,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_SHIFT_L_BIT|KMC_BIT_CLEAR,
|
|
KMC_ACT_DONE,
|
|
/* 2 */KMC_RECORDSTART, 9,
|
|
KMC_TEST_CODETABLE, 2, VK_SHIFT, VK_LSHIFT,
|
|
KMC_TEST_STATEBIT, GKEYSTATE_SHIFT_L_BIT|KMC_BIT_CLEAR,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_SHIFT_L_BIT,
|
|
KMC_ACT_DONE,
|
|
/* 3 */KMC_RECORDSTART, 7,
|
|
KMC_TEST_CODE, VK_RSHIFT,
|
|
KMC_TEST_LASTCODE, 0x00,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_SHIFT_R_BIT|KMC_BIT_CLEAR,
|
|
KMC_ACT_DONE,
|
|
/* 4 */KMC_RECORDSTART, 7,
|
|
KMC_TEST_CODE, VK_RSHIFT,
|
|
KMC_TEST_STATEBIT, GKEYSTATE_SHIFT_R_BIT|KMC_BIT_CLEAR,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_SHIFT_R_BIT,
|
|
KMC_ACT_DONE,
|
|
/* 5 */KMC_RECORDSTART, 9, // CONTROL (left & Right)
|
|
KMC_TEST_CODETABLE, 2, VK_CONTROL, VK_LCONTROL,
|
|
KMC_TEST_LASTCODE, 0x00,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_CTRL_L_BIT|KMC_BIT_CLEAR,
|
|
KMC_ACT_DONE,
|
|
/* 6 */KMC_RECORDSTART, 9,
|
|
KMC_TEST_CODETABLE, 2, VK_CONTROL, VK_LCONTROL,
|
|
KMC_TEST_STATEBIT, GKEYSTATE_CTRL_L_BIT|KMC_BIT_CLEAR,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_CTRL_L_BIT,
|
|
KMC_ACT_DONE,
|
|
/* 7 */KMC_RECORDSTART, 7,
|
|
KMC_TEST_CODE, VK_RCONTROL,
|
|
KMC_TEST_LASTCODE, 0x00,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_CTRL_R_BIT|KMC_BIT_CLEAR,
|
|
KMC_ACT_DONE,
|
|
/* 8 */KMC_RECORDSTART, 7,
|
|
KMC_TEST_CODE, VK_RCONTROL,
|
|
KMC_TEST_STATEBIT, GKEYSTATE_CTRL_R_BIT|KMC_BIT_CLEAR,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_CTRL_R_BIT,
|
|
KMC_ACT_DONE,
|
|
/* 9 */KMC_RECORDSTART, 9, // ALT (left & Right)
|
|
KMC_TEST_CODETABLE, 2, VK_MENU, VK_LMENU,
|
|
KMC_TEST_LASTCODE, 0x00,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_ALT_L_BIT|KMC_BIT_CLEAR,
|
|
KMC_ACT_DONE,
|
|
/* 10 */KMC_RECORDSTART, 9,
|
|
KMC_TEST_CODETABLE, 2, VK_MENU, VK_LMENU,
|
|
KMC_TEST_STATEBIT, GKEYSTATE_ALT_L_BIT|KMC_BIT_CLEAR,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_ALT_L_BIT,
|
|
KMC_ACT_DONE,
|
|
/* 11 */KMC_RECORDSTART, 7,
|
|
KMC_TEST_CODE, VK_RMENU,
|
|
KMC_TEST_LASTCODE, 0x00,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_ALT_R_BIT|KMC_BIT_CLEAR,
|
|
KMC_ACT_DONE,
|
|
/* 12 */KMC_RECORDSTART, 7,
|
|
KMC_TEST_CODE, VK_RMENU,
|
|
KMC_TEST_STATEBIT, GKEYSTATE_ALT_R_BIT|KMC_BIT_CLEAR,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_ALT_R_BIT,
|
|
KMC_ACT_DONE,
|
|
/* 13 */KMC_RECORDSTART, 9, // WinKey (left or right)
|
|
KMC_TEST_CODETABLE, 2, VK_LWIN, VK_RWIN,
|
|
KMC_TEST_LASTCODE, 0x00,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_WINKEY_BIT|KMC_BIT_CLEAR,
|
|
KMC_ACT_DONE,
|
|
/* 14 */KMC_RECORDSTART, 9,
|
|
KMC_TEST_CODETABLE, 2, VK_LWIN, VK_RWIN,
|
|
KMC_TEST_STATEBIT, GKEYSTATE_WINKEY_BIT|KMC_BIT_CLEAR,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_WINKEY_BIT,
|
|
KMC_ACT_DONE,
|
|
|
|
// Locking Shifters: CAPSLOCK, NUMLOCK and SCROLLLOCK
|
|
/* 15 */KMC_RECORDSTART, 7, // CAPSLOCK (keyup only)
|
|
KMC_TEST_CODE, VK_CAPITAL,
|
|
KMC_TEST_LASTCODE, 0x00,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_CAPSLOCK_BIT|KMC_BIT_INVERT,
|
|
KMC_ACT_DONE,
|
|
/* 16 */KMC_RECORDSTART, 7, // NUMLOCK (keyup only)
|
|
KMC_TEST_CODE, VK_NUMLOCK,
|
|
KMC_TEST_LASTCODE, 0x00,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_NUMLOCK_BIT|KMC_BIT_INVERT,
|
|
KMC_ACT_DONE,
|
|
/* 17 */KMC_RECORDSTART, 7, // SCROLLLOCK (keyup only)
|
|
KMC_TEST_CODE, VK_SCROLL,
|
|
KMC_TEST_LASTCODE, 0x00,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_SCROLLLOCK_BIT|KMC_BIT_INVERT,
|
|
KMC_ACT_DONE,
|
|
|
|
// Keyup, Repeat
|
|
/* 18 */KMC_RECORDSTART, 18, // Clear any shifter keys that got through
|
|
KMC_TEST_CODETABLE, 14, VK_SHIFT, VK_LSHIFT, VK_RSHIFT,
|
|
VK_CONTROL, VK_LCONTROL, VK_RCONTROL,
|
|
VK_MENU, VK_LMENU, VK_RMENU,
|
|
VK_LWIN, VK_RWIN,
|
|
VK_CAPITAL, VK_NUMLOCK, VK_SCROLL,
|
|
KMC_ACT_RESET,
|
|
KMC_ACT_STOP,
|
|
/* 19 */KMC_RECORDSTART, 4, // Skip special codes 0x00 (Keyup) & 0x01 (Repeat)
|
|
KMC_TEST_CODERANGE, 0x00, 0x01,
|
|
KMC_ACT_STOP,
|
|
/* 20 */KMC_RECORDSTART, 6, // Keyup
|
|
KMC_ACT_STATEBIT, GKEYSTATE_KEYUP_BIT|KMC_BIT_CLEAR,
|
|
KMC_TEST_LASTCODE, 0x00,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_KEYUP_BIT,
|
|
/* 21 */KMC_RECORDSTART, 6, // Repeat
|
|
KMC_ACT_STATEBIT, GKEYSTATE_REPEAT_BIT|KMC_BIT_CLEAR,
|
|
KMC_TEST_LASTCODE, 0x01,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_REPEAT_BIT,
|
|
|
|
// 0 - 9
|
|
/* 22 */KMC_RECORDSTART, 7, // Alt 0-9
|
|
KMC_TEST_ALT,
|
|
KMC_TEST_CODERANGE, '0', '9',
|
|
KMC_ACT_CHARADD, 10,
|
|
KMC_ACT_STOP,
|
|
/* 23 */KMC_RECORDSTART, 17, // Shifted 0-9
|
|
KMC_TEST_SHIFT,
|
|
KMC_TEST_CODERANGE, '0', '9',
|
|
KMC_ACT_CHARTABLE, 10, ')', '!', '@', '#', '$', '%', '^', '&', '*', '(',
|
|
KMC_ACT_DONE,
|
|
/* 24 */KMC_RECORDSTART, 5, // 0 - 9
|
|
KMC_TEST_CODERANGE, '0', '9',
|
|
KMC_ACT_CHARCODE,
|
|
KMC_ACT_DONE,
|
|
|
|
// A - Z
|
|
/* 25 */KMC_RECORDSTART, 7, // Control A-Z
|
|
KMC_TEST_CTRL,
|
|
KMC_TEST_CODERANGE, 'A', 'Z',
|
|
KMC_ACT_CHARRANGE, 1,
|
|
KMC_ACT_DONE,
|
|
/* 26 */KMC_RECORDSTART, 7, // No Caps A-Z
|
|
KMC_TEST_NOCAPS,
|
|
KMC_TEST_CODERANGE, 'A', 'Z',
|
|
KMC_ACT_CHARRANGE, 'a',
|
|
KMC_ACT_DONE,
|
|
/* 27 */KMC_RECORDSTART, 5, // Caps A-Z
|
|
KMC_TEST_CODERANGE, 'A', 'Z',
|
|
KMC_ACT_CHARCODE,
|
|
KMC_ACT_DONE,
|
|
|
|
// Number pad
|
|
/* 28 */KMC_RECORDSTART, 7, // Alt Number pad
|
|
KMC_TEST_ALT,
|
|
KMC_TEST_CODERANGE, VK_NUMPAD0, VK_NUMPAD9,
|
|
KMC_ACT_CHARADD, 10,
|
|
KMC_ACT_STOP,
|
|
/* 29 */KMC_RECORDSTART, 5,
|
|
KMC_TEST_ALT,
|
|
KMC_TEST_CODERANGE, VK_MULTIPLY, VK_DIVIDE,
|
|
KMC_ACT_STOP,
|
|
/* 30 */KMC_RECORDSTART, 7, // Number pad with Numlock
|
|
KMC_TEST_NUMLOCK,
|
|
KMC_TEST_CODERANGE, VK_NUMPAD0, VK_NUMPAD9,
|
|
KMC_ACT_CHARRANGE, '0',
|
|
KMC_ACT_DONE,
|
|
/* 31 */KMC_RECORDSTART, 13,
|
|
KMC_TEST_NUMLOCK,
|
|
KMC_TEST_CODERANGE, VK_MULTIPLY, VK_DIVIDE,
|
|
KMC_ACT_CHARTABLE, 6, '*', '+', GKEY_ENTER, '-', '.', '/',
|
|
KMC_ACT_DONE,
|
|
/* 32 */KMC_RECORDSTART, 4, // Number pad with no Numlock
|
|
KMC_TEST_CODE, VK_NUMPAD5,
|
|
KMC_ACT_RESET,
|
|
KMC_ACT_STOP,
|
|
/* 33 */KMC_RECORDSTART, 12,
|
|
KMC_TEST_CODERANGE, VK_MULTIPLY, VK_DIVIDE,
|
|
KMC_ACT_CHARTABLE, 6, '*', '+', GKEY_ENTER, '-', GKEY_DEL, '/',
|
|
KMC_ACT_DONE,
|
|
/* 34 */KMC_RECORDSTART, 18,
|
|
KMC_TEST_CODERANGE, VK_NUMPAD0, VK_NUMPAD9,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_SPECIAL_BIT,
|
|
KMC_ACT_CHARTABLE, 10, GKEY_INSERT, GKEY_END, GKEY_DOWN, GKEY_PAGEDOWN, GKEY_LEFT, '5', GKEY_RIGHT, GKEY_HOME, GKEY_UP, GKEY_PAGEUP,
|
|
KMC_ACT_DONE,
|
|
|
|
// Symbols
|
|
/* 35 */KMC_RECORDSTART, 14, // Shifted Symbols
|
|
KMC_TEST_SHIFT,
|
|
KMC_TEST_CODERANGE, VK_OEM_1, VK_OEM_3,
|
|
KMC_ACT_CHARTABLE, 7, ':', '+', '<', '_', '>', '?', '~',
|
|
KMC_ACT_DONE,
|
|
/* 36 */KMC_RECORDSTART, 11,
|
|
KMC_TEST_SHIFT,
|
|
KMC_TEST_CODERANGE, VK_OEM_4, VK_OEM_7,
|
|
KMC_ACT_CHARTABLE, 4, '{', '|', '}', '"',
|
|
KMC_ACT_DONE,
|
|
/* 37 */KMC_RECORDSTART, 13, // Non-shifted Symbols
|
|
KMC_TEST_CODERANGE, VK_OEM_1, VK_OEM_3,
|
|
KMC_ACT_CHARTABLE, 7, ';', '=', ',', '-', '.', '/', '`',
|
|
KMC_ACT_DONE,
|
|
/* 38 */KMC_RECORDSTART, 10,
|
|
KMC_TEST_CODERANGE, VK_OEM_4, VK_OEM_7,
|
|
KMC_ACT_CHARTABLE, 4, '[', '\\', ']', '\'',
|
|
KMC_ACT_DONE,
|
|
|
|
// Special Keys
|
|
// Extra special keys like Media and Browser keys are still to be implemented.
|
|
/* 39 */KMC_RECORDSTART, 17, // Normal Control Type Keys
|
|
KMC_TEST_CODETABLE, 6, VK_BACK, VK_TAB, VK_RETURN, VK_ESCAPE, VK_SPACE, VK_DELETE,
|
|
KMC_ACT_CHARTABLE, 6, GKEY_BACKSPACE, GKEY_TAB, GKEY_ENTER, GKEY_ESC, GKEY_SPACE, GKEY_DEL,
|
|
KMC_ACT_DONE,
|
|
/* 40 */KMC_RECORDSTART, 35, // Special Keys
|
|
KMC_TEST_CODETABLE, 14, VK_PRIOR, VK_NEXT,
|
|
VK_HOME, VK_END,
|
|
VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN,
|
|
VK_INSERT,
|
|
VK_SNAPSHOT, VK_SLEEP, VK_PAUSE, VK_CANCEL,
|
|
VK_APPS,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_SPECIAL_BIT,
|
|
KMC_ACT_CHARTABLE, 14, GKEY_PAGEUP, GKEY_PAGEDOWN,
|
|
GKEY_HOME, GKEY_END,
|
|
GKEY_LEFT, GKEY_RIGHT, GKEY_UP, GKEY_DOWN,
|
|
GKEY_INSERT,
|
|
GKEY_PRINTSCREEN, GKEY_SLEEP, GKEY_CTRLPAUSE, GKEY_CTRLBREAK,
|
|
GKEY_RIGHTCLICKKEY,
|
|
KMC_ACT_DONE,
|
|
/* 41 */KMC_RECORDSTART, 8, // F1 .. F15
|
|
KMC_TEST_CODERANGE, VK_F1, VK_F15,
|
|
KMC_ACT_STATEBIT, GKEYSTATE_SPECIAL_BIT,
|
|
KMC_ACT_CHARRANGE, GKEY_FN1,
|
|
KMC_ACT_DONE,
|
|
|
|
// Anything else
|
|
/* 40 */KMC_RECORDSTART, 1, // Just send the scan code to the user
|
|
KMC_ACT_DONE,
|
|
|
|
// EOF
|
|
KMC_RECORDSTART, 0
|
|
};
|
|
#elif !GKEYBOARD_LAYOUT_OFF
|
|
#if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT
|
|
#warning "The WIN32 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 WIN32 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 Win32KeyboardInit(GKeyboard *k, unsigned driverinstance);
|
|
static int Win32KeyboardGetData(GKeyboard *k, gU8 *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 Win32 keyboard layout
|
|
#if GKEYBOARD_WIN32_NO_LAYOUT
|
|
0,
|
|
#else
|
|
GKEYBOARD_WIN32_DEFAULT_LAYOUT,
|
|
#endif
|
|
|
|
Win32KeyboardInit, // init
|
|
0, // deinit
|
|
Win32KeyboardGetData, // getdata
|
|
0 // putdata void (*putdata)(GKeyboard *k, char ch); Optional
|
|
}};
|
|
|
|
static int keypos;
|
|
static gU8 keybuffer[8];
|
|
static GKeyboard *keyboard;
|
|
#endif
|
|
|
|
static DWORD winThreadId;
|
|
static volatile gBool QReady;
|
|
static HANDLE drawMutex;
|
|
static HWND hWndParent = 0;
|
|
|
|
/*===========================================================================*/
|
|
/* Driver local routines . */
|
|
/*===========================================================================*/
|
|
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE
|
|
#define WIN32_BUTTON_AREA 16
|
|
#else
|
|
#define WIN32_BUTTON_AREA 0
|
|
#endif
|
|
|
|
#define APP_NAME "uGFX"
|
|
|
|
typedef struct winPriv {
|
|
HWND hwnd;
|
|
HDC dcBuffer;
|
|
HBITMAP dcBitmap;
|
|
HBITMAP dcOldBitmap;
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
|
|
gCoord mousex, mousey;
|
|
gU16 mousebuttons;
|
|
GMouse *mouse;
|
|
gBool mouseenabled;
|
|
void (*capfn)(void * hWnd, GDisplay *g, gU16 buttons, gCoord x, gCoord y);
|
|
#endif
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE
|
|
gU8 toggles;
|
|
#endif
|
|
#if GDISP_HARDWARE_STREAM_WRITE || GDISP_HARDWARE_STREAM_READ
|
|
gCoord x0, y0, x1, y1;
|
|
gCoord x, y;
|
|
#endif
|
|
} winPriv;
|
|
|
|
void gfxEmulatorSetParentWindow(void *hwnd) {
|
|
hWndParent = (HWND)hwnd;
|
|
}
|
|
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
|
|
void gfxEmulatorMouseInject(GDisplay *g, gU16 buttons, gCoord x, gCoord y) {
|
|
winPriv * priv;
|
|
|
|
priv = (winPriv *)g->priv;
|
|
priv->mousebuttons = buttons;
|
|
priv->mousex = x;
|
|
priv->mousey = y;
|
|
if ((gmvmt(priv->mouse)->d.flags & GMOUSE_VFLG_NOPOLL)) // For normal setup this is always true
|
|
_gmouseWakeup(priv->mouse);
|
|
}
|
|
void gfxEmulatorMouseEnable(GDisplay *g, gBool enabled) {
|
|
((winPriv *)g->priv)->mouseenabled = enabled;
|
|
}
|
|
void gfxEmulatorMouseCapture(GDisplay *g, void (*capfn)(void * hWnd, GDisplay *g, gU16 buttons, gCoord x, gCoord y)) {
|
|
((winPriv *)g->priv)->capfn = capfn;
|
|
}
|
|
#endif
|
|
|
|
static LRESULT myWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HDC dc;
|
|
PAINTSTRUCT ps;
|
|
GDisplay * g;
|
|
winPriv * priv;
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
|
|
gU16 btns;
|
|
#endif
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE
|
|
HBRUSH hbrOn, hbrOff;
|
|
HPEN pen;
|
|
RECT rect;
|
|
HGDIOBJ old;
|
|
POINT p;
|
|
gCoord pos;
|
|
gU8 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 GFX_USE_GINPUT && (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 ((gCoord)HIWORD(lParam) < GDISP_SCREEN_HEIGHT) {
|
|
btns = priv->mousebuttons;
|
|
btns |= GINPUT_MOUSE_BTN_LEFT;
|
|
goto mousemove;
|
|
}
|
|
#endif
|
|
|
|
// Handle mouse down on the toggle area
|
|
#if GINPUT_NEED_TOGGLE
|
|
if ((gCoord)HIWORD(lParam) >= GDISP_SCREEN_HEIGHT && (g->flags & GDISP_FLG_HASTOGGLE)) {
|
|
bit = 1 << ((gCoord)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 == gDelayForever
|
|
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 == gDelayForever
|
|
ginputToggleWakeup();
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Handle mouse up on the window
|
|
#if GINPUT_NEED_MOUSE
|
|
if ((gCoord)HIWORD(lParam) < GDISP_SCREEN_HEIGHT) {
|
|
btns = priv->mousebuttons;
|
|
btns &= ~GINPUT_MOUSE_BTN_LEFT;
|
|
goto mousemove;
|
|
}
|
|
#endif
|
|
break;
|
|
#endif
|
|
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
|
|
case WM_MBUTTONDOWN:
|
|
g = (GDisplay *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
|
|
priv = (winPriv *)g->priv;
|
|
if ((gCoord)HIWORD(lParam) < GDISP_SCREEN_HEIGHT) {
|
|
btns = priv->mousebuttons;
|
|
btns |= GINPUT_MOUSE_BTN_MIDDLE;
|
|
goto mousemove;
|
|
}
|
|
break;
|
|
case WM_MBUTTONUP:
|
|
g = (GDisplay *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
|
|
priv = (winPriv *)g->priv;
|
|
if ((gCoord)HIWORD(lParam) < GDISP_SCREEN_HEIGHT) {
|
|
btns = priv->mousebuttons;
|
|
btns &= ~GINPUT_MOUSE_BTN_MIDDLE;
|
|
goto mousemove;
|
|
}
|
|
break;
|
|
case WM_RBUTTONDOWN:
|
|
g = (GDisplay *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
|
|
priv = (winPriv *)g->priv;
|
|
if ((gCoord)HIWORD(lParam) < GDISP_SCREEN_HEIGHT) {
|
|
btns = priv->mousebuttons;
|
|
btns |= GINPUT_MOUSE_BTN_RIGHT;
|
|
goto mousemove;
|
|
}
|
|
break;
|
|
case WM_RBUTTONUP:
|
|
g = (GDisplay *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
|
|
priv = (winPriv *)g->priv;
|
|
if ((gCoord)HIWORD(lParam) < GDISP_SCREEN_HEIGHT) {
|
|
btns = priv->mousebuttons;
|
|
btns &= ~GINPUT_MOUSE_BTN_RIGHT;
|
|
goto mousemove;
|
|
}
|
|
break;
|
|
case WM_MOUSEMOVE:
|
|
g = (GDisplay *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
|
|
priv = (winPriv *)g->priv;
|
|
if ((gCoord)HIWORD(lParam) >= GDISP_SCREEN_HEIGHT)
|
|
break;
|
|
btns = priv->mousebuttons;
|
|
|
|
mousemove:
|
|
if (priv->capfn)
|
|
priv->capfn(hWnd, g, btns, (gCoord)LOWORD(lParam), (gCoord)HIWORD(lParam));
|
|
if (priv->mouseenabled) {
|
|
priv->mousebuttons = btns;
|
|
priv->mousex = (gCoord)LOWORD(lParam);
|
|
priv->mousey = (gCoord)HIWORD(lParam);
|
|
if ((gmvmt(priv->mouse)->d.flags & GMOUSE_VFLG_NOPOLL)) // For normal setup this is always true
|
|
_gmouseWakeup(priv->mouse);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD
|
|
case WM_ACTIVATE:
|
|
// Copy the lock key states into the uGFX keyboard as it might have changed while we were away
|
|
// For simplicity we do this on both activate and deactivate.
|
|
if (keyboard && keyboard->pLayout) {
|
|
if (GetKeyState(VK_NUMLOCK) & 1)
|
|
keyboard->keystate |= GKEYSTATE_NUMLOCK;
|
|
else
|
|
keyboard->keystate &= ~GKEYSTATE_NUMLOCK;
|
|
if (GetKeyState(VK_CAPITAL) & 1)
|
|
keyboard->keystate |= GKEYSTATE_CAPSLOCK;
|
|
else
|
|
keyboard->keystate &= ~GKEYSTATE_CAPSLOCK;
|
|
if (GetKeyState(VK_SCROLL) & 1)
|
|
keyboard->keystate |= GKEYSTATE_SCROLLLOCK;
|
|
else
|
|
keyboard->keystate &= ~GKEYSTATE_SCROLLLOCK;
|
|
}
|
|
break;
|
|
case WM_SYSKEYDOWN:
|
|
case WM_SYSKEYUP:
|
|
case WM_KEYDOWN:
|
|
case WM_KEYUP:
|
|
// A layout is being used: Send scan codes to the keyboard buffer
|
|
if (keyboard && keyboard->pLayout && keypos < (int)sizeof(keybuffer)-1 && (wParam & 0xFF) >= VK_BACK) {
|
|
if (Msg == WM_KEYUP || Msg == WM_SYSKEYUP)
|
|
keybuffer[keypos++] = 0x00; // Keyup
|
|
else if (HIWORD(lParam) & KF_REPEAT)
|
|
keybuffer[keypos++] = 0x01; // Repeat
|
|
keybuffer[keypos++] = wParam;
|
|
if ((gkvmt(keyboard)->d.flags & GKEYBOARD_VFLG_NOPOLL)) // For normal setup this is always true
|
|
_gkeyboardWakeup(keyboard);
|
|
}
|
|
return 0;
|
|
case WM_CHAR:
|
|
// A layout is not being used: Send character codes to the keyboard buffer
|
|
if (keyboard && !keyboard->pLayout && keypos < (int)sizeof(keybuffer)) {
|
|
wchar_t w;
|
|
int len;
|
|
|
|
// Convert from a UTF16 character to a UTF8 string.
|
|
w = wParam;
|
|
len = WideCharToMultiByte(CP_UTF8, 0, &w, 1, (char *)(keybuffer+keypos), sizeof(keybuffer)-keypos, 0, 0);
|
|
keypos += len;
|
|
if (len && (gkvmt(keyboard)->d.flags & GKEYBOARD_VFLG_NOPOLL)) // For normal setup this is always true
|
|
_gkeyboardWakeup(keyboard);
|
|
}
|
|
return 0;
|
|
/*
|
|
case WM_DEADCHAR:
|
|
case WM_SYSCHAR:
|
|
case WM_SYSDEADCHAR:
|
|
break;
|
|
*/
|
|
#endif
|
|
|
|
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 GFX_USE_GINPUT && GINPUT_NEED_TOGGLE
|
|
if (ps.rcPaint.bottom >= GDISP_SCREEN_HEIGHT && (g->flags & GDISP_FLG_HASTOGGLE)) {
|
|
pen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
|
|
hbrOn = CreateSolidBrush(RGB(0, 0, 255));
|
|
hbrOff = CreateSolidBrush(RGB(128, 128, 128));
|
|
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 < GDISP_SCREEN_WIDTH; 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 DWORD WINAPI WindowThread(void *param) {
|
|
(void)param;
|
|
MSG msg;
|
|
|
|
// Establish this thread as a message queue thread
|
|
winThreadId = GetCurrentThreadId();
|
|
PeekMessage(&msg, 0, WM_USER, WM_USER, PM_NOREMOVE);
|
|
QReady = gTrue;
|
|
|
|
// Create the window class
|
|
{
|
|
WNDCLASS wc;
|
|
ATOM winClass;
|
|
|
|
wc.style = CS_HREDRAW | CS_VREDRAW; // | CS_OWNDC;
|
|
wc.lpfnWndProc = (WNDPROC)myWindowProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = GetModuleHandle(0);
|
|
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
|
|
wc.hCursor = LoadCursor(0, IDC_ARROW);
|
|
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
|
|
wc.lpszMenuName = 0;
|
|
wc.lpszClassName = APP_NAME;
|
|
winClass = RegisterClass(&wc);
|
|
assert(winClass != 0);
|
|
}
|
|
|
|
do {
|
|
// This is a high priority task - make sure other tasks get a go.
|
|
Sleep(1);
|
|
|
|
while(PeekMessage(&msg, 0, 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 GFX_USE_GINPUT && 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,
|
|
hWndParent, 0,
|
|
GetModuleHandle(0), g);
|
|
assert(msg.hwnd != 0);
|
|
|
|
// 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 gBool 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) {
|
|
HANDLE hth;
|
|
|
|
// Create the draw mutex
|
|
drawMutex = CreateMutex(0, FALSE, 0);
|
|
|
|
// Create the thread
|
|
if (!(hth = CreateThread(0, 0, WindowThread, 0, CREATE_SUSPENDED, 0)))
|
|
return gFalse;
|
|
SetThreadPriority(hth, THREAD_PRIORITY_ABOVE_NORMAL);
|
|
ResumeThread(hth);
|
|
CloseHandle(hth);
|
|
|
|
// Wait for our thread to be ready
|
|
while (!QReady)
|
|
Sleep(1);
|
|
}
|
|
|
|
// Initialise the GDISP structure
|
|
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;
|
|
|
|
// Turn on toggles for the first GINPUT_TOGGLE_CONFIG_ENTRIES windows
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE
|
|
if (g->controllerdisplay < GINPUT_TOGGLE_CONFIG_ENTRIES) {
|
|
g->flags |= GDISP_FLG_HASTOGGLE;
|
|
toggleWindow = g;
|
|
}
|
|
#endif
|
|
|
|
// Create a private area for this window
|
|
priv = gfxAlloc(sizeof(winPriv));
|
|
assert(priv != 0);
|
|
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);
|
|
|
|
// Create the associated mouse
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
|
|
priv->mouseenabled = hWndParent ? gFalse : gTrue;
|
|
priv->mouse = (GMouse *)gdriverRegister((const GDriverVMT*)GMOUSE_DRIVER_VMT, g);
|
|
#endif
|
|
|
|
sprintf(buf, APP_NAME " - %u", g->systemdisplay+1);
|
|
SetWindowTextA(priv->hwnd, buf);
|
|
ShowWindow(priv->hwnd, SW_SHOW);
|
|
UpdateWindow(priv->hwnd);
|
|
|
|
return gTrue;
|
|
}
|
|
|
|
#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) {
|
|
fprintf(stderr, "%s\n", msg);
|
|
}
|
|
#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 = gdispColor2Native(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 gOrientation0:
|
|
default:
|
|
x = priv->x;
|
|
y = priv->y;
|
|
break;
|
|
case gOrientation90:
|
|
x = priv->y;
|
|
y = g->g.Width - 1 - priv->x;
|
|
break;
|
|
case gOrientation180:
|
|
x = g->g.Width - 1 - priv->x;
|
|
y = g->g.Height - 1 - priv->y;
|
|
break;
|
|
case gOrientation270:
|
|
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 gColor 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 gOrientation0:
|
|
default:
|
|
color = GetPixel(priv->dcBuffer, priv->x, priv->y);
|
|
break;
|
|
case gOrientation90:
|
|
color = GetPixel(priv->dcBuffer, priv->y, g->g.Width - 1 - priv->x);
|
|
break;
|
|
case gOrientation180:
|
|
color = GetPixel(priv->dcBuffer, g->g.Width - 1 - priv->x, g->g.Height - 1 - priv->y);
|
|
break;
|
|
case gOrientation270:
|
|
color = GetPixel(priv->dcBuffer, g->g.Height - 1 - priv->y, priv->x);
|
|
break;
|
|
}
|
|
#else
|
|
color = GetPixel(priv->dcBuffer, priv->x, priv->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 gdispNative2Color(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 = gdispColor2Native(g->p.color);
|
|
|
|
#if GDISP_NEED_CONTROL
|
|
switch(g->g.Orientation) {
|
|
case gOrientation0:
|
|
default:
|
|
x = g->p.x;
|
|
y = g->p.y;
|
|
break;
|
|
case gOrientation90:
|
|
x = g->p.y;
|
|
y = g->g.Width - 1 - g->p.x;
|
|
break;
|
|
case gOrientation180:
|
|
x = g->g.Width - 1 - g->p.x;
|
|
y = g->g.Height - 1 - g->p.y;
|
|
break;
|
|
case gOrientation270:
|
|
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 = gdispColor2Native(g->p.color);
|
|
hbr = CreateSolidBrush(color);
|
|
|
|
#if GDISP_NEED_CONTROL
|
|
switch(g->g.Orientation) {
|
|
case gOrientation0:
|
|
default:
|
|
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 gOrientation90:
|
|
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 gOrientation180:
|
|
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 gOrientation270:
|
|
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 gPixel *rotateimg(GDisplay *g, const gPixel *buffer) {
|
|
gPixel *dstbuf;
|
|
gPixel *dst;
|
|
const gPixel *src;
|
|
size_t sz;
|
|
gCoord i, j;
|
|
|
|
// Allocate the destination buffer
|
|
sz = (size_t)g->p.cx * (size_t)g->p.cy;
|
|
if (!(dstbuf = (gPixel *)malloc(sz * sizeof(gPixel))))
|
|
return 0;
|
|
|
|
// Copy the bits we need
|
|
switch(g->g.Orientation) {
|
|
case gOrientation0:
|
|
default:
|
|
return 0; // not handled as it doesn't need to be.
|
|
case gOrientation90:
|
|
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 gOrientation180:
|
|
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 gOrientation270:
|
|
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
|
|
#if COLOR_SYSTEM != GDISP_COLORSYSTEM_TRUECOLOR || COLOR_TYPE_BITS <= 8
|
|
#error "GDISP Win32: This driver's bitblit currently only supports true-color with bit depths > 8 bits."
|
|
#endif
|
|
|
|
LLDSPEC void gdisp_lld_blit_area(GDisplay *g) {
|
|
winPriv * priv;
|
|
gPixel * buffer;
|
|
RECT rect;
|
|
BITMAPV4HEADER bmpInfo;
|
|
#if GDISP_NEED_CONTROL
|
|
gPixel* bufferBase;
|
|
#endif
|
|
|
|
// Make everything relative to the start of the line
|
|
priv = g->priv;
|
|
buffer = g->p.ptr;
|
|
buffer += g->p.x2 * g->p.y1 + g->p.x1;
|
|
#if GDISP_NEED_CONTROL
|
|
bufferBase = buffer; // Keep pointer to original buffer for correct free()-ing later on
|
|
#endif
|
|
|
|
memset(&bmpInfo, 0, sizeof(bmpInfo));
|
|
bmpInfo.bV4Size = sizeof(bmpInfo);
|
|
bmpInfo.bV4Planes = 1;
|
|
bmpInfo.bV4BitCount = COLOR_TYPE_BITS;
|
|
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 gOrientation0:
|
|
default:
|
|
bmpInfo.bV4SizeImage = (g->p.cy*g->p.x2) * sizeof(gPixel);
|
|
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 gOrientation90:
|
|
if (!(buffer = rotateimg(g, buffer))) return;
|
|
bmpInfo.bV4SizeImage = (g->p.cy*g->p.cx) * sizeof(gPixel);
|
|
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 gOrientation180:
|
|
if (!(buffer = rotateimg(g, buffer))) return;
|
|
bmpInfo.bV4SizeImage = (g->p.cy*g->p.cx) * sizeof(gPixel);
|
|
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 gOrientation270:
|
|
if (!(buffer = rotateimg(g, buffer))) return;
|
|
bmpInfo.bV4SizeImage = (g->p.cy*g->p.cx) * sizeof(gPixel);
|
|
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(gPixel);
|
|
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 (bufferBase != buffer)
|
|
free(buffer);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_HARDWARE_PIXELREAD
|
|
LLDSPEC gColor 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 gOrientation0:
|
|
default:
|
|
color = GetPixel(priv->dcBuffer, g->p.x, g->p.y);
|
|
break;
|
|
case gOrientation90:
|
|
color = GetPixel(priv->dcBuffer, g->p.y, g->g.Width - 1 - g->p.x);
|
|
break;
|
|
case gOrientation180:
|
|
color = GetPixel(priv->dcBuffer, g->g.Width - 1 - g->p.x, g->g.Height - 1 - g->p.y);
|
|
break;
|
|
case gOrientation270:
|
|
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 gdispNative2Color(color);
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_NEED_SCROLL && GDISP_HARDWARE_SCROLL
|
|
LLDSPEC void gdisp_lld_vertical_scroll(GDisplay *g) {
|
|
winPriv * priv;
|
|
RECT rect;
|
|
gCoord lines;
|
|
|
|
priv = g->priv;
|
|
|
|
#if GDISP_NEED_CONTROL
|
|
switch(g->g.Orientation) {
|
|
case gOrientation0:
|
|
default:
|
|
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 gOrientation90:
|
|
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 gOrientation180:
|
|
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 gOrientation270:
|
|
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 == (gOrientation)g->p.ptr)
|
|
return;
|
|
switch((gOrientation)g->p.ptr) {
|
|
case gOrientation0:
|
|
case gOrientation180:
|
|
g->g.Width = GDISP_SCREEN_WIDTH;
|
|
g->g.Height = GDISP_SCREEN_HEIGHT;
|
|
break;
|
|
case gOrientation90:
|
|
case gOrientation270:
|
|
g->g.Height = GDISP_SCREEN_WIDTH;
|
|
g->g.Width = GDISP_SCREEN_HEIGHT;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
g->g.Orientation = (gOrientation)g->p.ptr;
|
|
return;
|
|
/*
|
|
case GDISP_CONTROL_POWER:
|
|
case GDISP_CONTROL_BACKLIGHT:
|
|
case GDISP_CONTROL_CONTRAST:
|
|
*/
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
|
|
static gBool Win32MouseInit(GMouse *m, unsigned driverinstance) {
|
|
(void) m;
|
|
(void) driverinstance;
|
|
return gTrue;
|
|
}
|
|
static gBool Win32MouseRead(GMouse *m, GMouseReading *pt) {
|
|
GDisplay * g;
|
|
winPriv * priv;
|
|
|
|
g = m->display;
|
|
priv = g->priv;
|
|
|
|
pt->x = priv->mousex;
|
|
pt->y = priv->mousey;
|
|
pt->z = (priv->mousebuttons & GINPUT_MOUSE_BTN_LEFT) ? 1 : 0;
|
|
pt->buttons = priv->mousebuttons;
|
|
|
|
#if GDISP_NEED_CONTROL
|
|
// If the self-rotation has been set in the VMT then do that here (TESTING ONLY)
|
|
if ((gmvmt(m)->d.flags & GMOUSE_VFLG_SELFROTATION)) { // For normal setup this is always False
|
|
gCoord t;
|
|
|
|
switch(gdispGGetOrientation(m->display)) {
|
|
case gOrientation0:
|
|
default:
|
|
break;
|
|
case gOrientation90:
|
|
t = pt->x;
|
|
pt->x = g->g.Width - 1 - pt->y;
|
|
pt->y = t;
|
|
break;
|
|
case gOrientation180:
|
|
pt->x = g->g.Width - 1 - pt->x;
|
|
pt->y = g->g.Height - 1 - pt->y;
|
|
break;
|
|
case gOrientation270:
|
|
t = pt->y;
|
|
pt->y = g->g.Height - 1 - pt->x;
|
|
pt->x = t;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return gTrue;
|
|
}
|
|
#endif /* GINPUT_NEED_MOUSE */
|
|
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD
|
|
static gBool Win32KeyboardInit(GKeyboard *k, unsigned driverinstance) {
|
|
(void) driverinstance;
|
|
|
|
// Only one please
|
|
if (keyboard)
|
|
return gFalse;
|
|
|
|
keyboard = k;
|
|
return gTrue;
|
|
}
|
|
|
|
static int Win32KeyboardGetData(GKeyboard *k, gU8 *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
|
|
|
|
#if GFX_USE_GINPUT && GINPUT_NEED_TOGGLE
|
|
#if GINPUT_TOGGLE_CONFIG_ENTRIES > 1
|
|
#error "GDISP Win32: GINPUT_TOGGLE_CONFIG_ENTRIES must be 1 until Toggles can use GDriver"
|
|
#endif
|
|
|
|
const 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];
|
|
((GToggleConfig *)ptc)->id = 0;
|
|
|
|
// We have 8 buttons per window.
|
|
((GToggleConfig *)ptc)->mask = 0xFF;
|
|
|
|
// No inverse or special mode
|
|
((GToggleConfig *)ptc)->invert = 0x00;
|
|
((GToggleConfig *)ptc)->mode = 0;
|
|
}
|
|
unsigned ginput_lld_toggle_getbits(const GToggleConfig *ptc) {
|
|
(void) ptc;
|
|
|
|
// This should use ID
|
|
if (!toggleWindow)
|
|
return 0;
|
|
return ((winPriv *)toggleWindow->priv)->toggles;
|
|
//return ((winPriv *)((GDisplay *)(ptc->id))->priv)->toggles;
|
|
}
|
|
#endif /* GINPUT_NEED_TOGGLE */
|
|
|
|
#endif /* GFX_USE_GDISP */
|