winamp/Src/Wasabi/api/wnd/keyboard.cpp
2024-09-24 14:54:57 +02:00

570 lines
14 KiB
C++

#include <precomp.h>
#include "keyboard.h"
#include <api/locales/localesmgr.h>
//#include <api/wac/main.h> // CUT !!!
#include <api/script/objects/systemobj.h>
#include <api/wnd/wndtrack.h>
#ifdef WASABI_COMPILE_SCRIPT
#include <api/script/vcpu.h>
#endif
#if !defined(WIN32) && !defined(LINUX)
#error port me
#endif
Keyboard::vkEntry Keyboard::vkEntries[]={
#ifdef WIN32
//1, "lbutton", // fg> we don't want mouse messages in keyboard events, no.
//2, "rbutton",
3, L"cancel",
//4, L"mbutton",
8, L"backspace",
9, L"tab",
0xc, L"clear",
0xd, L"return",
0x10, L"shift",
0x11, L"ctrl",
0x12, L"alt",
0x13, L"pause",
0x14, L"capslock",
0x1b, L"esc",
0x20, L"space",
0x21, L"pgup",
0x22, L"pgdn",
0x23, L"end",
0x24, L"home",
0x25, L"left",
0x26, L"up",
0x27, L"right",
0x28, L"down",
0x29, L"select",
0x2b, L"execute",
0x2c, L"prtscr",
0x2d, L"insert",
0x2e, L"del",
0x2f, L"help",
0x5b, L"lwin",
0x5c, L"rwin",
0x5d, L"mwin",
0x60, L"n0",
0x61, L"n1",
0x62, L"n2",
0x63, L"n3",
0x64, L"n4",
0x65, L"n5",
0x66, L"n6",
0x67, L"n7",
0x68, L"n8",
0x69, L"n9",
0x6a, L"numpad_multiply",
0x6b, L"numpad_add",
0x6c, L"separator",
0x6d, L"numpad_substract",
0x6e, L"numpad_point",
0x6f, L"numpad_divide",
0x70, L"f1",
0x71, L"f2",
0x72, L"f3",
0x73, L"f4",
0x74, L"f5",
0x75, L"f6",
0x76, L"f7",
0x77, L"f8",
0x78, L"f9",
0x79, L"f10",
0x7a, L"f11",
0x7b, L"f12",
0x90, L"numlock",
0x91, L"scroll",
0xbb, L"equal",
0xbd, L"minus",
0xbf, L"slash",
// 0xdb, L"minus", // seems to be the french code? --BU
0xf6, L"attn",
0xfe, L"clear",
#endif
#ifdef LINUX
{XK_space, L"space"},
{XK_Cancel, L"cancel"},
{XK_BackSpace, L"backspace"},
{XK_Tab, L"tab"},
{XK_Clear, L"clear"},
{XK_Return, L"return"},
{XK_Shift_L, L"shift"},
{XK_Shift_R, L"shift"},
{XK_Control_L, L"ctrl"},
{XK_Control_R, L"ctrl"},
{XK_Alt_L, L"alt"},
{XK_Alt_R, L"alt"},
{XK_Pause, L"pause"},
{XK_Caps_Lock, L"capslock"},
{XK_Escape, L"esc"},
{XK_Page_Up, L"pgup"},
{XK_KP_Page_Up, L"pgup"},
{XK_Page_Down, L"pgdn"},
{XK_KP_Page_Down, L"pgdn"},
{XK_End, L"end"},
{XK_KP_End, L"end"},
{XK_Home, L"home"},
{XK_KP_Home, L"home"},
{XK_Left, L"left"},
{XK_KP_Left, L"left"},
{XK_Up, L"up"},
{XK_KP_Up, L"up"},
{XK_Right, L"right"},
{XK_KP_Right, L"right"},
{XK_Down, L"down"},
{XK_KP_Down, L"down"},
{XK_Select, L"select"},
{XK_Execute, L"execute"},
{XK_Print, L"prtscr"},
{XK_Insert, L"insert"},
{XK_KP_Insert, L"insert"},
{XK_Delete, L"del"},
{XK_KP_Delete, L"del"},
{XK_Help, L"help"},
{XK_KP_0, L"n0"},
{XK_KP_1, L"n1"},
{XK_KP_2, L"n2"},
{XK_KP_3, L"n3"},
{XK_KP_4, L"n4"},
{XK_KP_5, L"n5"},
{XK_KP_6, L"n6"},
{XK_KP_7, L"n7"},
{XK_KP_8, L"n8"},
{XK_KP_9, L"n9"},
{XK_KP_Multiply, L"numpad_multiply"},
{XK_KP_Add, L"numpad_add"},
{XK_KP_Separator, L"separator"},
{XK_KP_Subtract, L"numpad_substract"},
{XK_KP_Decimal, L"numpad_point"},
{XK_KP_Divide, L"numpad_divide"},
{XK_F1, L"f1"},
{XK_F2, L"f2"},
{XK_F3, L"f3"},
{XK_F4, L"f4"},
{XK_F5, L"f5"},
{XK_F6, L"f6"},
{XK_F7, L"f7"},
{XK_F8, L"f8"},
{XK_F9, L"f9"},
{XK_F10, L"f10"},
{XK_F11, L"f11"},
{XK_F12, L"f12"},
{XK_Scroll_Lock, L"scroll"},
#ifdef XK_3270
{XK_3270_Attn, L"attn"}, // I don't know what this is...
#endif
{XK_Clear, L"clear"},
#endif
};
//PORTME
wchar_t *Keyboard::getVkName(int vkey)
{
if(vkey>=0x41 && vkey<=0x5A) { // letters
static wchar_t key[2];
key[0]=vkey+0x20;
key[1]=0;
return key;
}
if(vkey>=0x30 && vkey<=0x39) { // numbers
static wchar_t key[2];
key[0]=vkey;
key[1]=0;
return key;
}
for(int i=0;i<(sizeof(vkEntries)/sizeof(vkEntry));i++) {
if(vkEntries[i].vk==vkey) return vkEntries[i].trans;
}
#ifdef _DEBUG
//DebugString("undefined vk key pressed! (0x%x) :(\n",vkey);
#endif
return NULL;
}
int Keyboard::forwardKbdMessage(ifc_window *from, int msg, int wp, int lp)
{
OSWINDOWHANDLE wnd_to = WASABI_API_WND->main_getRootWnd()->gethWnd();
// note to self:
// this is necessary for winamp2's TranslateAccelerator call to work, it seems that this function will
// use the mouse capture wnd to determine the keyboard focus, oh thank you microsoft, because of you
// a script has to use "complete;" to be able to detect shift+ctrl+alt + click on a toggle button,
// otherwise pressing a key will throw the capture away, and the button will think the mouse is gone.
// this means that we can't be stealth doing that, we have to prevent anybody else getting shift+ctrl+alt
// isn't that nice ?
// we still avoid doing that if a mouse button is down, this will allow key+drags with capture
#ifdef WIN32
if (!(GetAsyncKeyState(VK_LBUTTON)&(1 << 31)) && !(GetAsyncKeyState(VK_RBUTTON)&(1 << 31)) && !(GetAsyncKeyState(VK_MBUTTON)&(1 << 31))) {
SetCapture(NULL);
}
#endif
#ifdef GET_KBDFORWARD_WND
ifc_window *dp = from->getDesktopParent();
if (dp) {
Layout *l = static_cast<Layout *>(dp->getInterface(layoutGuid));
if (l) {
Container *c = l->getParentContainer();
if (c) {
GUID g = c->getDefaultContent();
GET_KBDFORWARD_WND(g, wnd_to);
}
}
}
#endif
if (infw) return 0;
// if (from && from->gethWnd() == wnd_to) return 1;
infw = 1;
/* MSG winmsg;
winmsg.message = msg;
winmsg.hwnd = from->gethWnd();
winmsg.wParam = wp;
winmsg.lParam = lp;*/
int r = 0;
// int r = WASABI_API_APP->app_translateAccelerators(&winmsg);
infw = 0;
return r;
}
int Keyboard::onForwardOnChar(ifc_window *from, unsigned int c, int kd)
{
if (WASABI_API_WND->isKeyboardLocked()) return 1;
return forwardKbdMessage(from, WM_CHAR, (WPARAM)c, (LPARAM)kd);
}
int MEMCMPC(void *m, char c, int size) {
char *p = (char*)m;
for (int i=0;i<size;i++) {
if (*p != c) return 1;
}
return 0;
}
int Keyboard::onForwardOnKeyDown(ifc_window *from, int k, int kd, int nomsg)
{
if (WASABI_API_WND->isKeyboardLocked()) return 1;
if (infw) return 0;
if (k >= MAX_KEY) return 0;
lastwasreset = 0;
pressedKeys[k]=1;
syncKeyTable();
wchar_t s[64]={0,};
int first=1;
#ifdef LINUX
for (int i=MAX_KEY-1; i >= 0; i--) {
#else
for (int i=0;i<MAX_KEY;i++) {
#endif
if (pressedKeys[i]) {
wchar_t *n = getVkName(i);
if (n) {
if (!first) wcscat(s, L"+");
else first=0;
wcscat(s,n);
}
}
}
ifc_window *wnd = from;
if(s[0]) {
#ifdef _DEBUG
DebugString("keyboard: key pressed: %s\n",s);
#endif
#ifdef WASABI_COMPILE_LOCALES
const wchar_t *action;
#endif
int found=0;
while(wnd!=NULL) {
for(int i=0;i<accSecEntries.getNumItems();i++) {
AccSec *ase = accSecEntries[i];
if(ase->global || ase->wnd==wnd) {
#ifdef WASABI_COMPILE_LOCALES
if (action=LocalesManager::translateAccelerator(ase->name, s))
{
if(ase->wnd==wnd) found = 1;
wnd->onAcceleratorEvent(action);
#ifdef _DEBUG
DebugString("keyboard: accelerator found\n");
#endif
continue;
}
#else
wnd->onAcceleratorEvent(s);
#endif
}
}
wnd=wnd->getParent();
}
if (found) return 1;
if (NULL != from)
{
const wchar_t *accelSec = from->getId();
if (accelSec && *accelSec)
{
#ifdef WASABI_COMPILE_LOCALES
if(action=LocalesManager::translateAccelerator(accelSec, s))
{
int r = 0;
#ifdef WASABI_COMPILE_SCRIPT
r = SystemObject::onAccelerator(action, accelSec, s);
#endif
#ifdef WASABI_COMPILE_ACTIONS
if (r == 0)
{
int act=SkinParser::getAction(action);
if(act) Main::doAction(act);
}
#endif
#ifdef _DEBUG
DebugString("keyboard: accelerator found\n");
#endif
return 1;
}
#endif
}
}
#ifdef WASABI_COMPILE_LOCALES
if(action=LocalesManager::translateAccelerator(L"general", s))
{
int r = 0;
#ifdef WASABI_COMPILE_SCRIPT
r = SystemObject::onAccelerator(action, L"general", s);
#endif
#ifdef WASABI_COMPILE_ACTIONS
if (r == 0) {
int act=SkinParser::getAction(action);
if(act) Main::doAction(act);
}
#endif
#ifdef _DEBUG
DebugString("keyboard: accelerator found\n");
#endif
return 1;
}
#endif
#ifdef _DEBUG
DebugString("keyboard: accelerator not found\n");
#endif
#ifdef WASABI_COMPILE_SCRIPT
DebugStringW(L"keyboard: sending \"%s\" to script\n", s);
SystemObject::onKeyDown(s);
if (VCPU::getComplete()) {
DebugStringW(L"keyboard: %s trapped by script\n", s);
return 1;
}
#endif
if (pressedKeys[VK_CONTROL] && pressedKeys[VK_TAB])
{
int next = pressedKeys[VK_SHIFT] ? -1 : 1;
HWND w = GetForegroundWindow();
if (w == NULL) {
WASABI_API_WND->main_getRootWnd()->setFocus();
return 1;
}
ifc_window *cur = windowTracker->rootWndFromHwnd(w); // TODO: API_WNDMGR->
if (cur != NULL) {
ifc_window *nextwnd = windowTracker->getNextDesktopWindow(cur, next);
if (nextwnd) nextwnd->setFocus();
return 1;
}
WASABI_API_WND->main_getRootWnd()->setFocus();
return 1;
}
if (from && pressedKeys[VK_CONTROL] && pressedKeys[VK_F4]) {
ifc_window *dp = from->getDesktopParent();
if (dp) {
Layout *l = static_cast<Layout *>(dp->getInterface(layoutGuid));
if (l) {
Container *c = l->getParentContainer();
if (c) {
if (c->isMainContainer())
c->setVisible(!c->isVisible());
else
c->close();
return 1;
}
}
}
}
if (pressedKeys[0x5D]) {
#if defined(WA3COMPATIBILITY)
Main::appContextMenu(from, TRUE, 0);
#elif defined(WASABI_CUSTOM_CONTEXTMENUS)
extern void appContextMenu(ifc_window *wnd);
appContextMenu(windowTracker->rootWndFromHwnd(GetForegroundWindow()));
#endif
}
if (s[0] && from) return forwardKbdMessage(from, WM_KEYDOWN, (WPARAM)k, (LPARAM)kd);
}
return 0;
}
void Keyboard::syncKeyTable() {
for (int i=0;i<MAX_KEY;i++) {
//if (pressedKeys[i] && !(GetAsyncKeyState(i) & (1 << 31))) pressedKeys[i] = 0;
if (pressedKeys[i] && !Std::keyDown(i)) pressedKeys[i] = 0;
}
}
int Keyboard::onForwardOnKeyUp(ifc_window *from, int k, int kd) {
if (WASABI_API_WND->isKeyboardLocked()) return 1;
if (infw) return 0;
if (k >= MAX_KEY) return 0;
/*int hadkey = */MEMCMPC(pressedKeys, 0, sizeof(pressedKeys));
pressedKeys[k]=0;
syncKeyTable();
wchar_t s[64]={0,};
int first=1;
#ifdef LINUX
for (int i=MAX_KEY-1; i >= 0; i--) {
#else
for (int i=0;i<MAX_KEY;i++) {
#endif
if (pressedKeys[i]) {
wchar_t *n = getVkName(i);
if (n) {
if (!first) wcscat(s, L"+");
else first=0;
wcscat(s,n);
}
}
}
if (!*s) {
if (!lastwasreset)
{
lastwasreset = 1;
#ifdef WASABI_COMPILE_SCRIPT
DebugStringW(L"keyboard: sending \"%s\" to script\n", s);
SystemObject::onKeyDown(s);
if (VCPU::getComplete()) {
DebugStringW(L"keyboard: %s trapped by script\n", s);
return 1;
}
#endif
}
}
return forwardKbdMessage(from, WM_KEYUP, (WPARAM)k, (LPARAM)kd);
}
int Keyboard::onForwardOnSysKeyDown(ifc_window *from, int k, int kd) {
if (WASABI_API_WND->isKeyboardLocked()) return 1;
if (infw) return 0;
if(kd&(1<<29)) pressedKeys[0x12]=1;
int r = onForwardOnKeyDown(from, k, 1);
if (r == 0) {
if (from && forwardKbdMessage(from, WM_SYSKEYDOWN, (WPARAM)k, (LPARAM)kd)) return 1;
}
return r;
}
int Keyboard::onForwardOnSysKeyUp(ifc_window *from, int k, int kd)
{
if (WASABI_API_WND->isKeyboardLocked()) return 1;
if (infw) return 0;
if(kd&(1<<29)) pressedKeys[0x12]=0;
pressedKeys[k]=0;
int r = onForwardOnKeyUp(from, k, 1);
if (r == 0) {
if (forwardKbdMessage(from, WM_SYSKEYUP, (WPARAM)k, (WPARAM)kd)) return 1;
}
return r;
}
int Keyboard::onForwardOnKillFocus() {
// FG> I don't think this is necessary anymore because onkeydown always resyncs the pressedKeys table
// and supressing this allows scripts to trap ctrl/alt/shit + clicks (otherwise the click would reset
// the modifiers by way of an automatic focus)
//MEMSET(pressedKeys,0,sizeof(pressedKeys));
return 0;
}
void Keyboard::registerAcceleratorSection(const wchar_t *name, ifc_window *wnd, int global)
{
accSecEntries.addItem(new AccSec(name,wnd,global));
viewer.viewItem(wnd);
}
int Keyboard::interceptOnChar(unsigned int c) {
if (hookers.getNumItems() > 0) {
return hookers.getLast()->onChar(c);
}
return 0;
}
int Keyboard::interceptOnKeyDown(int k){
if (hookers.getNumItems() > 0) {
return hookers.getLast()->onKeyDown(k);
}
return 0;
}
int Keyboard::interceptOnKeyUp(int k){
if (hookers.getNumItems() > 0) {
return hookers.getLast()->onKeyUp(k);
}
return 0;
}
int Keyboard::interceptOnSysKeyDown(int k, int kd){
if (hookers.getNumItems() > 0) {
return hookers.getLast()->onSysKeyDown(k, kd);
}
return 0;
}
int Keyboard::interceptOnSysKeyUp(int k, int kd){
if (hookers.getNumItems() > 0) {
return hookers.getLast()->onSysKeyUp(k, kd);
}
return 0;
}
void Keyboard::hookKeyboard(ifc_window *hooker) {
hookers.addItem(hooker);
DebugString("hookKeyboard = %d\n", hookers.getNumItems());
}
void Keyboard::unhookKeyboard(ifc_window *hooker) {
hookers.removeItem(hooker);
DebugString("unhookKeyboard = %d\n", hookers.getNumItems());
}
void Keyboard::reset() {
if (lastwasreset) return;
DebugString("keyboard reset\n");
MEMZERO(pressedKeys, sizeof(pressedKeys));
if (!lastwasreset) {
lastwasreset = 1;
#ifdef WASABI_COMPILE_SCRIPT
DebugString("keyboard: sending \"\" to script\n");
SystemObject::onKeyDown(L"");
#endif
}
}
int AccSecViewer::viewer_onItemDeleted(ifc_window *item) {
for(int i=0;i<Keyboard::accSecEntries.getNumItems();i++)
if(Keyboard::accSecEntries[i]->wnd==item) {
Keyboard::accSecEntries.removeByPos(i);
i--;
}
return 1;
}
wchar_t Keyboard::pressedKeys[MAX_KEY]={0,};
PtrList<AccSec> Keyboard::accSecEntries;
AccSecViewer Keyboard::viewer;
PtrList<ifc_window> Keyboard::hookers;
int Keyboard::infw = 0;
int Keyboard::lastwasreset = 0;