2015-04-12 07:07:38 +00:00
|
|
|
/*
|
|
|
|
* 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 src/gwin/gwin_keyboard.c
|
|
|
|
* @brief GWIN sub-system virtual keyboard code
|
|
|
|
*/
|
|
|
|
|
2015-11-21 09:27:08 +00:00
|
|
|
#include "../../gfx.h"
|
2015-04-12 07:07:38 +00:00
|
|
|
|
|
|
|
#if GFX_USE_GWIN && GWIN_NEED_KEYBOARD
|
|
|
|
|
|
|
|
#include "gwin_class.h"
|
|
|
|
#include "gwin_keyboard_layout.h"
|
|
|
|
|
|
|
|
typedef uint8_t utf8;
|
|
|
|
typedef uint16_t utf16;
|
|
|
|
typedef uint32_t utf32;
|
|
|
|
|
|
|
|
// A character code - note this is not UTF-32 but a representation of the UTF-8 code stream for a single character.
|
|
|
|
typedef uint32_t ucode;
|
|
|
|
|
2015-08-16 14:18:54 +00:00
|
|
|
static GSourceHandle AllKeyboards;
|
|
|
|
|
2015-04-12 07:07:38 +00:00
|
|
|
// Get the length of a UTF-8 string
|
|
|
|
static int UTF8StrLen(const utf8 *s) {
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = 0;
|
|
|
|
if (s) {
|
|
|
|
while (*s) {
|
|
|
|
len++;
|
|
|
|
if (!(s[0] & 0x80))
|
|
|
|
s++;
|
|
|
|
else if ((s[0] & 0xE0) == 0xC0 && (s[1] & 0xC0) == 0x80)
|
|
|
|
s+=2;
|
|
|
|
else if ((s[0] & 0xF0) == 0xE0 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80)
|
|
|
|
s+=3;
|
|
|
|
else if ((s[0] & 0xF8) == 0xF0 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80 && (s[3] & 0xC0) == 0x80)
|
|
|
|
s+=4;
|
|
|
|
else
|
|
|
|
// Invalid UTF-8 sequence - assume a single byte
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the nth character of a UTF8 string
|
|
|
|
static ucode UTF8CharAt(const utf8 *s, int n) {
|
|
|
|
ucode u;
|
|
|
|
|
|
|
|
u = 0;
|
|
|
|
if (!s) return 0;
|
|
|
|
|
|
|
|
while(*s) {
|
|
|
|
if (!(s[0] & 0x80)) {
|
|
|
|
u = s[0];
|
|
|
|
s++;
|
|
|
|
} else if ((s[0] & 0xE0) == 0xC0 && (s[1] & 0xC0) == 0x80) {
|
|
|
|
u = s[1] | ((ucode)s[0] << 8);
|
|
|
|
s+=2;
|
|
|
|
} else if ((s[0] & 0xF0) == 0xE0 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80) {
|
|
|
|
u = s[2] | ((ucode)s[1] << 8) | ((ucode)s[0] << 16);
|
|
|
|
s+=3;
|
|
|
|
} else if ((s[0] & 0xF8) == 0xF0 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80 && (s[3] & 0xC0) == 0x80) {
|
|
|
|
u = s[3] | ((ucode)s[2] << 8) | ((ucode)s[1] << 16) | ((ucode)s[0] << 24);
|
|
|
|
s+=4;
|
|
|
|
} else {
|
|
|
|
// Invalid UTF-8 sequence - assume a single byte
|
|
|
|
u = s[0];
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
if (--n < 0)
|
|
|
|
return u;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert a ucode to a UTF8 string (with no NULL on the end). Returns the number of bytes.
|
|
|
|
static unsigned UCode2UTF8(utf8 *dst, ucode u) {
|
|
|
|
if (!(u & 0xFFFFFF00)) {
|
|
|
|
dst[0] = u;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (!(u & 0xFFFF0000)) {
|
|
|
|
dst[0] = u >> 8;
|
|
|
|
dst[1] = u;
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
if (!(u & 0xFF000000)) {
|
|
|
|
dst[0] = u >> 16;
|
|
|
|
dst[1] = u >> 8;
|
|
|
|
dst[2] = u;
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
dst[0] = u >> 24;
|
|
|
|
dst[1] = u >> 16;
|
|
|
|
dst[2] = u >> 8;
|
|
|
|
dst[3] = u;
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int NumKeyRows(const char **keyset) {
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = 0;
|
|
|
|
while(*keyset++)
|
|
|
|
len++;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SendKeyboardEventToListener(GSourceListener *psl, GKeyboardObject *gk) {
|
|
|
|
GEventKeyboard *pe;
|
|
|
|
const GVSpecialKey *skey;
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
// If there is no event buffer just mark a missed event
|
|
|
|
if (!(pe = (GEventKeyboard *)geventGetEventBuffer(psl))) {
|
|
|
|
// This listener is missing - save the meta events that have happened
|
|
|
|
psl->srcflags |= GKEYSTATE_MISSED_EVENT;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The virtual keyboard can't generate repeats
|
|
|
|
//if ((psl->listenflags & GLISTEN_KEYREPEATSOFF) && (k->keystate & GKEYSTATE_REPEAT))
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// The virtual keyboard can't generate special keys
|
|
|
|
//if ((psl->listenflags & GLISTEN_KEYNOSPECIALS) && (k->keystate & GKEYSTATE_SPECIAL))
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// The virtual keyboard treats a key release as a keydown
|
|
|
|
//if (!(psl->listenflags & GLISTEN_KEYUP) && (k->keystate & GKEYSTATE_KEYUP))
|
|
|
|
// k->cntc = 0;
|
|
|
|
|
|
|
|
// The virtual keyboard has no transitions
|
|
|
|
//if (!(psl->listenflags & GLISTEN_KEYTRANSITIONS) && !k->cntc)
|
|
|
|
// return;
|
|
|
|
|
|
|
|
pe->type = GEVENT_KEYBOARD;
|
|
|
|
if (gk->key < 0x20) {
|
|
|
|
skey = &gk->keytable->skeys[gk->key-1];
|
|
|
|
for(i=0; skey->sendkey[i]; i++)
|
|
|
|
pe->c[i] = skey->sendkey[i];
|
|
|
|
} else
|
|
|
|
i = UCode2UTF8((utf8 *)pe->c, gk->key);
|
|
|
|
pe->bytecount = i;
|
|
|
|
for(; i < 8; i++)
|
|
|
|
pe->c[i] = 0;
|
|
|
|
pe->keystate = psl->srcflags;
|
|
|
|
psl->srcflags = 0;
|
|
|
|
geventSendEvent(psl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SendKeyboardEvent(GKeyboardObject *gk) {
|
|
|
|
GSourceListener *psl;
|
|
|
|
|
2015-08-16 14:18:54 +00:00
|
|
|
// Send to the "All Keyboards" source listeners
|
|
|
|
psl = 0;
|
|
|
|
while ((psl = geventGetSourceListener(AllKeyboards, psl)))
|
|
|
|
SendKeyboardEventToListener(psl, gk);
|
|
|
|
|
2015-04-12 07:07:38 +00:00
|
|
|
// Send to the keyboard specific source listeners
|
|
|
|
psl = 0;
|
|
|
|
while ((psl = geventGetSourceListener((GSourceHandle)gk, psl)))
|
|
|
|
SendKeyboardEventToListener(psl, gk);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if GINPUT_NEED_MOUSE
|
|
|
|
// Find the key from the keyset and the x, y position
|
2015-06-08 04:14:40 +00:00
|
|
|
static void KeyFindKey(GKeyboardObject *gk, coord_t x, coord_t y) {
|
2015-04-12 07:07:38 +00:00
|
|
|
const utf8 *krow;
|
|
|
|
fixed f;
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
if (x < 0 || y < 0 || x >= gk->w.g.width || y >= gk->w.g.height) {
|
2015-06-08 04:14:40 +00:00
|
|
|
gk->keyrow = gk->keycol = GKEY_BAD_ROWCOL;
|
2015-04-12 07:07:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the y parameters
|
|
|
|
f = FIXED(gk->w.g.height) / NumKeyRows(gk->keyset);
|
|
|
|
gk->keyrow = FIXED(y) / f;
|
|
|
|
gk->keyy = NONFIXED(f * gk->keyrow + FIXED0_5);
|
|
|
|
gk->keycy = NONFIXED(f * (gk->keyrow+1) + FIXED0_5) - gk->keyy;
|
|
|
|
|
|
|
|
// Get the current row
|
|
|
|
krow = (const utf8 *)gk->keyset[gk->keyrow];
|
|
|
|
|
|
|
|
// Get the x parameters
|
|
|
|
f = FIXED(gk->w.g.width) / UTF8StrLen(krow);
|
|
|
|
gk->keycol = FIXED(x) / f;
|
|
|
|
|
|
|
|
// Get the key
|
|
|
|
gk->key = UTF8CharAt(krow, gk->keycol);
|
|
|
|
|
|
|
|
// Amalgamate identical keys into one big key
|
|
|
|
idx = gk->keycol;
|
|
|
|
while(gk->keycol > 0 && UTF8CharAt(krow, gk->keycol-1) == gk->key)
|
|
|
|
gk->keycol--;
|
|
|
|
while(UTF8CharAt(krow, ++idx) == gk->key);
|
|
|
|
gk->keyx = NONFIXED(f * gk->keycol + FIXED0_5);
|
|
|
|
gk->keycx = NONFIXED(f * idx + FIXED0_5) - gk->keyx;
|
|
|
|
}
|
|
|
|
|
|
|
|
// A mouse up has occurred (it may or may not be over the button)
|
2015-06-08 04:14:40 +00:00
|
|
|
static void KeyMouseUp(GWidgetObject *gw, coord_t x, coord_t y) {
|
2015-04-12 07:07:38 +00:00
|
|
|
#define gk ((GKeyboardObject *)gw)
|
|
|
|
|
2015-06-08 04:14:40 +00:00
|
|
|
KeyFindKey(gk, x, y);
|
2015-04-12 07:07:38 +00:00
|
|
|
|
|
|
|
// Do we have a valid key?
|
2015-06-08 04:14:40 +00:00
|
|
|
if (gk->keyrow == GKEY_BAD_ROWCOL) {
|
|
|
|
if (gk->lastkeyrow != GKEY_BAD_ROWCOL) {
|
2015-04-12 07:07:38 +00:00
|
|
|
gw->g.flags |= GKEYBOARD_FLG_QUICKUPDATE;
|
|
|
|
_gwinUpdate((GHandle)gw);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We are turning off the display of the key
|
2015-06-08 04:14:40 +00:00
|
|
|
gk->keyrow = gk->keycol = GKEY_BAD_ROWCOL;
|
2015-04-12 07:07:38 +00:00
|
|
|
|
|
|
|
// Is this one of the special keys
|
|
|
|
if (gk->key < 0x20) {
|
|
|
|
// This is a special key
|
|
|
|
const GVSpecialKey *skey;
|
|
|
|
|
|
|
|
skey = &gk->keytable->skeys[gk->key - 1];
|
|
|
|
|
|
|
|
if ((skey->flags & GVKEY_SINGLESET)) {
|
|
|
|
// Single character switch to a new layout
|
|
|
|
gk->keyset = gk->keytable->ksets[skey->newset];
|
|
|
|
gk->w.g.flags &= ~(GKEYBOARD_FLG_QUICKUPDATE|GKEYBOARD_FLG_REVERTSET);
|
|
|
|
gk->w.g.flags |= GKEYBOARD_FLG_REVERTSET;
|
|
|
|
|
|
|
|
} else if ((skey->flags & GVKEY_LOCKSET)) {
|
|
|
|
// Locked switch to a new layout
|
|
|
|
gk->keyset = gk->keytable->ksets[skey->newset];
|
|
|
|
gk->w.g.flags &= ~(GKEYBOARD_FLG_QUICKUPDATE|GKEYBOARD_FLG_REVERTSET);
|
|
|
|
|
|
|
|
} else if ((gk->w.g.flags & GKEYBOARD_FLG_REVERTSET)) {
|
|
|
|
// Revert to default layout
|
|
|
|
gk->keyset = gk->keytable->ksets[0];
|
|
|
|
gk->w.g.flags &= ~(GKEYBOARD_FLG_QUICKUPDATE|GKEYBOARD_FLG_REVERTSET);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Just turning off a key
|
|
|
|
gw->g.flags |= GKEYBOARD_FLG_QUICKUPDATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the key if required
|
|
|
|
if (skey->sendkey && skey->sendkey[0])
|
|
|
|
SendKeyboardEvent(gk);
|
|
|
|
|
|
|
|
// Update the display
|
|
|
|
_gwinUpdate((GHandle)gw);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do we need to revert to the standard layout?
|
|
|
|
if ((gk->w.g.flags & GKEYBOARD_FLG_REVERTSET)) {
|
|
|
|
gk->keyset = gk->keytable->ksets[0];
|
|
|
|
gk->w.g.flags &= ~(GKEYBOARD_FLG_QUICKUPDATE|GKEYBOARD_FLG_REVERTSET);
|
|
|
|
} else {
|
|
|
|
gw->g.flags |= GKEYBOARD_FLG_QUICKUPDATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the key
|
|
|
|
SendKeyboardEvent(gk);
|
|
|
|
|
|
|
|
// Update the display
|
|
|
|
_gwinUpdate((GHandle)gw);
|
|
|
|
}
|
|
|
|
|
|
|
|
// A mouse move has occurred (it may or may not be over the button)
|
2015-06-08 04:14:40 +00:00
|
|
|
static void KeyMouseMove(GWidgetObject *gw, coord_t x, coord_t y) {
|
2015-04-12 07:07:38 +00:00
|
|
|
#define gk ((GKeyboardObject *)gw)
|
|
|
|
|
2015-06-08 04:14:40 +00:00
|
|
|
KeyFindKey(gk, x, y);
|
2015-04-12 07:07:38 +00:00
|
|
|
|
|
|
|
if (gk->keyrow != gk->lastkeyrow || gk->keycol != gk->lastkeycol) {
|
|
|
|
gk->w.g.flags |= GKEYBOARD_FLG_QUICKUPDATE;
|
|
|
|
_gwinUpdate((GHandle)gw);
|
|
|
|
}
|
|
|
|
#undef gk
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-11-15 11:26:04 +00:00
|
|
|
extern const GVKeyTable GWIN_KEYBOARD_DEFAULT_LAYOUT;
|
2015-04-12 07:07:38 +00:00
|
|
|
|
|
|
|
// The button VMT table
|
|
|
|
static const gwidgetVMT keyboardVMT = {
|
|
|
|
{
|
|
|
|
"VKeyboard", // The classname
|
|
|
|
sizeof(GKeyboardObject), // The object size
|
|
|
|
_gwidgetDestroy, // The destroy routine
|
|
|
|
_gwidgetRedraw, // The redraw routine
|
|
|
|
0, // The after-clear routine
|
|
|
|
},
|
|
|
|
gwinKeyboardDraw_Normal, // The default drawing routine
|
|
|
|
#if GINPUT_NEED_MOUSE
|
|
|
|
{
|
2015-06-08 04:14:40 +00:00
|
|
|
KeyMouseMove, // Process mouse down events
|
|
|
|
KeyMouseUp, // Process mouse up events
|
|
|
|
KeyMouseMove, // Process mouse move events
|
2015-04-12 07:07:38 +00:00
|
|
|
},
|
|
|
|
#endif
|
2015-08-16 14:18:54 +00:00
|
|
|
#if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD
|
2015-08-12 15:32:38 +00:00
|
|
|
{
|
|
|
|
0 // Process keyboard events
|
|
|
|
},
|
|
|
|
#endif
|
2015-04-12 07:07:38 +00:00
|
|
|
#if GINPUT_NEED_TOGGLE
|
|
|
|
{
|
|
|
|
0, // No toggle roles
|
|
|
|
0, // Assign Toggles
|
|
|
|
0, // Get Toggles
|
|
|
|
0, // Process toggle off events
|
|
|
|
0, // Process toggle on events
|
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#if GINPUT_NEED_DIAL
|
|
|
|
{
|
|
|
|
0, // No dial roles
|
|
|
|
0, // Assign Dials (NOT USED)
|
|
|
|
0, // Get Dials (NOT USED)
|
|
|
|
0, // Process dial move events (NOT USED)
|
|
|
|
},
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
GHandle gwinGKeyboardCreate(GDisplay *g, GKeyboardObject *gk, const GWidgetInit *pInit) {
|
|
|
|
if (!(gk = (GKeyboardObject *)_gwidgetCreate(g, &gk->w, pInit, &keyboardVMT)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
gk->keytable = &GWIN_KEYBOARD_DEFAULT_LAYOUT;
|
|
|
|
gk->keyset = gk->keytable->ksets[0];
|
2015-06-08 04:14:40 +00:00
|
|
|
gk->lastkeyrow = gk->lastkeycol = gk->keyrow = gk->keycol = GKEY_BAD_ROWCOL;
|
2015-08-16 14:18:54 +00:00
|
|
|
|
|
|
|
if (!AllKeyboards)
|
|
|
|
AllKeyboards = ginputGetKeyboard(GKEYBOARD_ALL_INSTANCES);
|
|
|
|
|
2015-04-12 07:07:38 +00:00
|
|
|
gwinSetVisible((GHandle)gk, pInit->g.show);
|
|
|
|
return (GHandle)gk;
|
|
|
|
}
|
|
|
|
|
|
|
|
GSourceHandle gwinKeyboardGetEventSource(GHandle gh) {
|
|
|
|
if (gh->vmt != (gwinVMT *)&keyboardVMT)
|
|
|
|
return 0;
|
|
|
|
return (GSourceHandle)gh;
|
|
|
|
}
|
|
|
|
|
2015-11-24 21:29:34 +00:00
|
|
|
void gwinKeyboardSetLayout(GHandle gh, const struct GVKeyTable *layout) {
|
2015-04-12 07:07:38 +00:00
|
|
|
#define gk ((GKeyboardObject *)gh)
|
|
|
|
|
|
|
|
if (gh->vmt != (gwinVMT *)&keyboardVMT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!layout)
|
|
|
|
layout = &GWIN_KEYBOARD_DEFAULT_LAYOUT;
|
|
|
|
gk->keytable = layout;
|
|
|
|
gk->keyset = gk->keytable->ksets[0];
|
2015-06-08 04:14:40 +00:00
|
|
|
gk->lastkeyrow = gk->lastkeycol = gk->keyrow = gk->keycol = GKEY_BAD_ROWCOL;
|
2015-04-12 07:07:38 +00:00
|
|
|
gk->w.g.flags &= ~(GKEYBOARD_FLG_QUICKUPDATE|GKEYBOARD_FLG_REVERTSET);
|
|
|
|
gwinRedraw(gh);
|
|
|
|
#undef gk
|
|
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
|
|
* Custom Draw Routines
|
|
|
|
*----------------------------------------------------------*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
static const GColorSet *getDrawColors(GWidgetObject *gw) {
|
|
|
|
if (!(gw->g.flags & GWIN_FLG_SYSENABLED)) return &gw->pstyle->disabled;
|
|
|
|
if ((gw->g.flags & GBUTTON_FLG_PRESSED)) return &gw->pstyle->pressed;
|
|
|
|
return &gw->pstyle->enabled;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
void gwinKeyboardDraw_Normal(GWidgetObject *gw, void *param) {
|
2015-12-10 20:58:12 +00:00
|
|
|
#define gk ((GKeyboardObject *)gw)
|
2015-04-12 07:07:38 +00:00
|
|
|
|
2015-12-10 20:58:12 +00:00
|
|
|
char cap[5];
|
2015-04-12 07:07:38 +00:00
|
|
|
const char *pcap;
|
|
|
|
const utf8 *krow;
|
2015-12-10 20:58:12 +00:00
|
|
|
coord_t x, y, cx, cy;
|
|
|
|
uint8_t rows, cols, row, col, kcols;
|
|
|
|
ucode key;
|
|
|
|
fixed fx, fy;
|
|
|
|
const GColorSet *pcol;
|
|
|
|
|
2015-04-12 07:07:38 +00:00
|
|
|
(void) param;
|
|
|
|
|
2015-12-10 20:58:12 +00:00
|
|
|
// Make sure that this is a keyboard widget object
|
|
|
|
if (gw->g.vmt != (gwinVMT *)&keyboardVMT)
|
|
|
|
return;
|
2015-04-12 07:07:38 +00:00
|
|
|
|
|
|
|
// Get the y parameters
|
|
|
|
rows = NumKeyRows(gk->keyset);
|
|
|
|
fy = FIXED(gk->w.g.height) / rows;
|
2015-12-10 20:58:12 +00:00
|
|
|
for (row = 0; row < rows; row++) {
|
2015-04-12 07:07:38 +00:00
|
|
|
y = NONFIXED(fy * row + FIXED0_5);
|
|
|
|
cy = NONFIXED(fy * (row+1) + FIXED0_5) - y;
|
|
|
|
|
|
|
|
// Get the current row
|
|
|
|
krow = (const utf8 *)gk->keyset[row];
|
|
|
|
|
|
|
|
// Get the x parameters
|
|
|
|
cols = UTF8StrLen(krow);
|
|
|
|
fx = FIXED(gk->w.g.width) / cols;
|
2015-12-10 20:58:12 +00:00
|
|
|
for (col = 0; col < cols; col=kcols) {
|
2015-04-12 07:07:38 +00:00
|
|
|
|
2015-12-10 20:58:12 +00:00
|
|
|
// Get the correct color set
|
2015-04-12 07:07:38 +00:00
|
|
|
if (!(gk->w.g.flags & GWIN_FLG_SYSENABLED))
|
2015-12-10 20:58:12 +00:00
|
|
|
pcol = &gk->w.pstyle->disabled;
|
|
|
|
else
|
2015-04-12 07:07:38 +00:00
|
|
|
pcol = &gk->w.pstyle->enabled;
|
2015-12-10 20:58:12 +00:00
|
|
|
|
2015-04-12 07:07:38 +00:00
|
|
|
// Get the key
|
|
|
|
key = UTF8CharAt(krow, col);
|
2015-12-10 20:58:12 +00:00
|
|
|
|
|
|
|
// Fuse identical keys into one big key
|
2015-04-12 07:07:38 +00:00
|
|
|
kcols = col+1;
|
2015-12-10 20:58:12 +00:00
|
|
|
while (UTF8CharAt(krow, kcols) == key)
|
2015-04-12 07:07:38 +00:00
|
|
|
kcols++;
|
2015-12-10 20:58:12 +00:00
|
|
|
|
|
|
|
// If quick update needed and keyboard already drawn (if not use this flag, then bug when screen touched before keyboard was drawn)
|
|
|
|
if ( (gk->w.g.flags & GKEYBOARD_FLG_QUICKUPDATE) && !(gk->w.g.flags & GWIN_FLG_BGREDRAW) ) {
|
|
|
|
|
|
|
|
// If key pressed
|
|
|
|
if ( (gk->keyrow != GKEY_BAD_ROWCOL) && (gk->keycol != GKEY_BAD_ROWCOL) ) {
|
|
|
|
|
|
|
|
// And previous key have
|
|
|
|
if ( (gk->lastkeyrow != GKEY_BAD_ROWCOL) && (gk->lastkeycol != GKEY_BAD_ROWCOL) ) {
|
|
|
|
|
|
|
|
if (gk->lastkeyrow == row && gk->lastkeycol == col) {
|
|
|
|
// If keyboard has no "disabled" color
|
|
|
|
if (pcol != &gk->w.pstyle->disabled)
|
|
|
|
pcol = &gk->w.pstyle->enabled;
|
|
|
|
gk->lastkeyrow = gk->lastkeycol = GKEY_BAD_ROWCOL;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no previous key
|
|
|
|
else {
|
|
|
|
|
|
|
|
if (gk->keyrow == row && gk->keycol == col) {
|
|
|
|
if (pcol != &gk->w.pstyle->disabled)
|
|
|
|
pcol = &gk->w.pstyle->pressed;
|
|
|
|
gk->lastkeyrow = row;
|
|
|
|
gk->lastkeycol = col;
|
|
|
|
}
|
|
|
|
else if (gk->lastkeyrow == row && gk->lastkeycol == col)
|
|
|
|
{
|
|
|
|
if (pcol != &gk->w.pstyle->disabled) pcol = &gk->w.pstyle->enabled;
|
|
|
|
}
|
|
|
|
else continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If key up, and need clear the previous key
|
|
|
|
else if ( (gk->lastkeyrow != GKEY_BAD_ROWCOL) && (gk->lastkeycol != GKEY_BAD_ROWCOL) )
|
|
|
|
{
|
|
|
|
if ( (gk->lastkeyrow == row) && (gk->lastkeycol == col) )
|
|
|
|
{
|
|
|
|
if (pcol != &gk->w.pstyle->disabled) pcol = &gk->w.pstyle->enabled;
|
|
|
|
}
|
|
|
|
else continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gk->lastkeyrow = gk->lastkeycol = GKEY_BAD_ROWCOL;
|
|
|
|
}
|
|
|
|
|
2015-04-12 07:07:38 +00:00
|
|
|
x = NONFIXED(fx * col + FIXED0_5);
|
|
|
|
cx = NONFIXED(fx * kcols + FIXED0_5) - x;
|
2015-12-10 20:58:12 +00:00
|
|
|
|
2015-04-12 07:07:38 +00:00
|
|
|
if (key < 0x20) {
|
|
|
|
pcap = gk->keytable->skeys[key-1].keycap;
|
|
|
|
} else {
|
|
|
|
cap[UCode2UTF8((utf8 *)cap, key)] = 0;
|
|
|
|
pcap = cap;
|
|
|
|
}
|
2015-12-10 20:58:12 +00:00
|
|
|
|
|
|
|
switch(*pcap) {
|
|
|
|
|
|
|
|
case '\001': // Shift (up-arrow)
|
|
|
|
gdispGFillArea(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, pcol->fill);
|
|
|
|
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x +cx/4, gw->g.y+y+cy/2, gw->g.x+x+cx/2, gw->g.y+y +cy/4, pcol->text); /* / \ */
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+cx -cx/4, gw->g.y+y+cy/2, gw->g.x+x+cx/2, gw->g.y+y +cy/4, pcol->text);
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x +cx/4, gw->g.y+y+cy/2, gw->g.x+x+cx/2-cx/6, gw->g.y+y+cy/2, pcol->text); /* _ _ */
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+cx -cx/4, gw->g.y+y+cy/2, gw->g.x+x+cx/2+cx/6, gw->g.y+y+cy/2, pcol->text);
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+cx/2-cx/6, gw->g.y+y+cy/2, gw->g.x+x+cx/2-cx/6, gw->g.y+y+cy -cy/3, pcol->text); /* || */
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+cx/2+cx/6, gw->g.y+y+cy/2, gw->g.x+x+cx/2+cx/6, gw->g.y+y+cy -cy/3, pcol->text);
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+cx/2-cx/6, gw->g.y+y+cy -cy/3, gw->g.x+x+cx/2+cx/6, gw->g.y+y+cy -cy/3, pcol->text); /* _ */
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\002': // Shift locked (underlined up-arrow)
|
|
|
|
gdispGFillArea(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, pcol->fill);
|
|
|
|
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x +cx/4, gw->g.y+y+cy/2, gw->g.x+x+cx/2, gw->g.y+y +cy/4, pcol->text); /* / \ */
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+cx -cx/4, gw->g.y+y+cy/2, gw->g.x+x+cx/2, gw->g.y+y +cy/4, pcol->text);
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x +cx/4, gw->g.y+y+cy/2, gw->g.x+x+cx/2-cx/6, gw->g.y+y+cy/2, pcol->text); /* _ _ */
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+cx -cx/4, gw->g.y+y+cy/2, gw->g.x+x+cx/2+cx/6, gw->g.y+y+cy/2, pcol->text);
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+cx/2-cx/6, gw->g.y+y+cy/2, gw->g.x+x+cx/2-cx/6, gw->g.y+y+cy -cy/3, pcol->text); /* || */
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+cx/2+cx/6, gw->g.y+y+cy/2, gw->g.x+x+cx/2+cx/6, gw->g.y+y+cy -cy/3, pcol->text);
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+cx/2-cx/6, gw->g.y+y+cy -cy/3, gw->g.x+x+cx/2+cx/6, gw->g.y+y+cy -cy/3, pcol->text); /* _ */
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+cx/2-cx/5, gw->g.y+y+cy -cy/4, gw->g.x+x+cx/2+cx/5, gw->g.y+y+cy -cy/4, pcol->text); /* ___ */
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\t': // Tabulator
|
|
|
|
gdispGFillArea(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, pcol->fill);
|
|
|
|
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+1, gw->g.y+y+1, gw->g.x+x+cx-1, gw->g.y+y+cy/2, pcol->text);
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+1, gw->g.y+y+cy-1, gw->g.x+x+cx-1, gw->g.y+y+cy/2, pcol->text);
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+cx-1, gw->g.y+y+1, gw->g.x+x+cx-1, gw->g.y+y+cy-1, pcol->text);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\b': // Backspace
|
|
|
|
gdispGFillArea(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, pcol->fill);
|
|
|
|
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+ cx/8, gw->g.y+y+cy/2, gw->g.x+x+cx/2, gw->g.y+y +cy/3, pcol->text); /* / */
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+ cx/8, gw->g.y+y+cy/2, gw->g.x+x+cx-cx/8, gw->g.y+y+cy/2, pcol->text); /* -- */
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+ cx/8, gw->g.y+y+cy/2, gw->g.x+x+cx/2, gw->g.y+y+cy -cy/3, pcol->text); /* \ */
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '\r': // Enter
|
|
|
|
gdispGFillArea(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, pcol->fill);
|
|
|
|
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+(cx/3)*2, gw->g.y+y+cy/2, gw->g.x+x+(cx/3)*2, gw->g.y+y+cy/5, pcol->text); /* | */
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+ cx/3, gw->g.y+y+cy/2, gw->g.x+x+cx/3 +cx/8, gw->g.y+y+cy/3, pcol->text); /* / */
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+ cx/3, gw->g.y+y+cy/2, gw->g.x+x+(cx/3)*2, gw->g.y+y+cy/2, pcol->text); /* -- */
|
|
|
|
gdispGDrawLine(gw->g.display, gw->g.x+x+ cx/3, gw->g.y+y+cy/2, gw->g.x+x+cx/3 +cx/8, gw->g.y+y+cy -cy/3, pcol->text); /* \ */
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: // Regular character
|
|
|
|
gdispGFillStringBox(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, pcap, gw->g.font, pcol->text, pcol->fill, justifyCenter);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw the frame (border around the entire widget)
|
|
|
|
gdispGDrawBox(gw->g.display, gw->g.x+x, gw->g.y+y, cx, cy, pcol->edge);
|
|
|
|
|
|
|
|
// If key up and we already cleared the previous key
|
|
|
|
if ( (gk->keyrow == GKEY_BAD_ROWCOL) && (gk->keycol == GKEY_BAD_ROWCOL) && (gk->lastkeyrow == row) && (gk->lastkeycol == col) ) {
|
|
|
|
gk->lastkeyrow = gk->lastkeycol = GKEY_BAD_ROWCOL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Just quit the cycle if we did all the work in order not to waste any CPU time
|
|
|
|
if ( (row >= gk->keyrow && col >= gk->keycol) && (row >= gk->lastkeyrow && col >= gk->lastkeycol) ) {
|
|
|
|
return;
|
|
|
|
}
|
2015-04-12 07:07:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef gk
|
|
|
|
}
|
|
|
|
|
2015-08-16 14:18:54 +00:00
|
|
|
#if !(GFX_USE_GINPUT && GINPUT_NEED_KEYBOARD)
|
|
|
|
GSourceHandle ginputGetKeyboard(unsigned instance) {
|
|
|
|
if (instance == GKEYBOARD_ALL_INSTANCES)
|
|
|
|
return (GSourceHandle)&AllKeyboards;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-04-12 07:07:38 +00:00
|
|
|
#endif /* GFX_USE_GWIN && GWIN_NEED_KEYBOARD */
|