Skip to content

Commit 526ba08

Browse files
committed
Add support for mouse and keyboard in the JS/Emscripten port
Hooks up input to the workerApi JS functions exposed. Since we don't have threads, the CUDA event loop is replaced by periodic polling every 256K CPU instructions (roughly 120 Hz at current execution speeds)
1 parent f241830 commit 526ba08

8 files changed

Lines changed: 212 additions & 26 deletions

File tree

src/io/cuda/cuda.cc

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "system/sys.h"
4646
#include "system/sysclk.h"
4747
#include "system/systhread.h"
48+
#include "system/ui/gui.h"
4849

4950
#include "cuda.h"
5051

@@ -844,6 +845,7 @@ static bool doProcessCudaEvent(const SystemEvent &ev)
844845
{
845846
switch (ev.type) {
846847
case sysevKey: {
848+
IO_CUDA_TRACE("keyboard event: keycode=%s pressed=%s\n", ev.key.keycode, ev.key.pressed ? "true" : "false");
847849
uint8 k = ev.key.keycode;
848850
if (!ev.key.pressed) {
849851
k |= 0x80;
@@ -874,11 +876,12 @@ static bool doProcessCudaEvent(const SystemEvent &ev)
874876
}
875877
if (!ev.mouse.button2) dx |= 0x80;
876878
if (!ev.mouse.button1) dy |= 0x80;
877-
// ht_printf("adb mouse: cur: %d, %d d: %d, %d\n", ev.mouseEvent.x, ev.mouseEvent.y, dx, dy);
879+
IO_CUDA_TRACE("mouse event: cur: %d, %d d: %x, %x\n", ev.mouse.relx, ev.mouse.rely, dx, dy);
878880
cuda_send_packet(ADB_PACKET, 4, 0x40, 0x3c, dy, dx);
879881
return true;
880882
}
881883
default:
884+
IO_CUDA_WARN("unknown event type: %d\n", ev.type);
882885
return false;
883886
}
884887
}
@@ -921,23 +924,30 @@ static bool tryProcessCudaEvent(const SystemEvent &ev)
921924
return false;
922925
}
923926

927+
static void processCudaEvents()
928+
{
929+
// IO_CUDA_WARN("waiting on semaphore\n");
930+
sys_wait_semaphore(gCUDAEventSem);
931+
// IO_CUDA_WARN("semaphore signalled\n");
932+
SystemEventObject *seo;
933+
while ((seo = (SystemEventObject*)gCUDAEvents.deQueue())) {
934+
tryProcessCudaEvent(seo->mEv);
935+
delete seo;
936+
}
937+
}
938+
939+
#ifndef EMSCRIPTEN
924940
static void *cudaEventLoop(void *arg)
925941
{
926942
gKeyboard->attachEventHandler(cudaEventHandler);
927943
gMouse->attachEventHandler(cudaEventHandler);
928944
sys_lock_semaphore(gCUDAEventSem);
929945
while (1) {
930-
// IO_CUDA_WARN("waiting on semaphore\n");
931-
sys_wait_semaphore(gCUDAEventSem);
932-
// IO_CUDA_WARN("semaphore signalled\n");
933-
SystemEventObject *seo;
934-
while ((seo = (SystemEventObject*)gCUDAEvents.deQueue())) {
935-
tryProcessCudaEvent(seo->mEv);
936-
delete seo;
937-
}
946+
processCudaEvents();
938947
}
939948
return NULL;
940949
}
950+
#endif
941951

942952
bool cuda_prom_get_key(uint32 &key)
943953
{
@@ -980,8 +990,12 @@ void cuda_init()
980990
IO_CUDA_ERR("Can't create semaphore\n");
981991
}
982992

993+
#ifdef EMSCRIPTEN
994+
sys_gui_cuda_hook(cudaEventHandler, processCudaEvents);
995+
#else
983996
sys_thread cudaEventLoopThread;
984997
sys_create_thread(&cudaEventLoopThread, 0, cudaEventLoop, NULL);
998+
#endif
985999
}
9861000

9871001
void cuda_done()

src/system/ui/gui.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ bool sys_gui_open_file_dialog(String &ret, const String &title, const String &fi
3939
int sys_gui_messagebox(const String &title, const String &text, int buttons);
4040

4141
void sys_gui_event();
42+
43+
#ifdef EMSCRIPTEN
44+
#include "system/event.h"
45+
4246
void sys_gui_cpu_ops_hook(uint ops);
47+
void sys_gui_cuda_hook(SystemEventHandler cudaEventHandler, void (*processCudaEvents)());
48+
#endif
49+
4350

4451
#endif

src/system/ui/js/Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ AUTOMAKE_OPTIONS = foreign
22

33
noinst_LIBRARIES = libui.a
44

5-
libui_a_SOURCES = gui.cc sysdisplay.cc
5+
libui_a_SOURCES = gui.cc sysdisplay.cc syskeyboard.cc sysmouse.cc
66

77
AM_CPPFLAGS = -I ../../..

src/system/ui/js/gui.cc

Lines changed: 126 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
#include <emscripten.h>
12
#include "system/display.h"
3+
#include "system/keyboard.h"
4+
#include "system/mouse.h"
25
#include "system/ui/gui.h"
36

47
#include "sysjs.h"
58

9+
static void (*gProcessCudaEvents)();
10+
611
void sys_gui_init() {}
712

813
bool sys_gui_open_file_dialog(String &ret, const String &title, const String &filespec, const String &filespecname, const String &home, bool existing) {
@@ -15,24 +20,141 @@ int sys_gui_messagebox(const String &title, const String &text, int buttons) {
1520
return 0; // Placeholder return value
1621
}
1722

23+
// doProcessCudaEvent actually expects each mouse event to have the full state
24+
// (position and buttons), so keep track of what we sent to when we get updates.
25+
static SystemEvent gLastMouseEvent;
26+
27+
static void sys_gui_poll_events() {
28+
int lock = EM_ASM_INT_V({ return workerApi.acquireInputLock(); });
29+
if (!lock) {
30+
return;
31+
}
32+
33+
int mouseButtonState = EM_ASM_INT_V({
34+
return workerApi.getInputValue(workerApi.InputBufferAddresses.mouseButtonStateAddr);
35+
});
36+
int mouseButton2State = EM_ASM_INT_V({
37+
return workerApi.getInputValue(workerApi.InputBufferAddresses.mouseButton2StateAddr);
38+
});
39+
if (mouseButtonState > -1 || mouseButton2State > -1) {
40+
SystemEvent ev;
41+
ev.type = sysevMouse;
42+
ev.mouse.type = mouseButtonState == 1 || mouseButton2State == 1 ? sme_buttonPressed : sme_buttonReleased;
43+
44+
if (mouseButtonState != -1) {
45+
ev.mouse.button1 = mouseButtonState == 1;
46+
gLastMouseEvent.mouse.button1 = ev.mouse.button1;
47+
} else {
48+
ev.mouse.button1 = gLastMouseEvent.mouse.button1;
49+
}
50+
if (mouseButton2State != -1) {
51+
ev.mouse.button2 = mouseButton2State == 1;
52+
gLastMouseEvent.mouse.button2 = ev.mouse.button2;
53+
} else {
54+
ev.mouse.button2 = gLastMouseEvent.mouse.button2;
55+
}
56+
ev.mouse.button3 = false;
57+
58+
// Make sure we don't generate position updates
59+
ev.mouse.x = gLastMouseEvent.mouse.x;
60+
ev.mouse.y = gLastMouseEvent.mouse.y;
61+
ev.mouse.relx = 0;
62+
ev.mouse.rely = 0;
63+
gMouse->handleEvent(ev);
64+
}
65+
66+
int hasMousePosition = EM_ASM_INT_V({
67+
return workerApi.getInputValue(workerApi.InputBufferAddresses.mousePositionFlagAddr);
68+
});
69+
if (hasMousePosition) {
70+
SystemEvent ev;
71+
ev.type = sysevMouse;
72+
ev.mouse.type = sme_motionNotify;
73+
ev.mouse.relx = EM_ASM_INT_V({
74+
return workerApi.getInputValue(workerApi.InputBufferAddresses.mouseDeltaXAddr);
75+
});
76+
ev.mouse.rely = EM_ASM_INT_V({
77+
return workerApi.getInputValue(workerApi.InputBufferAddresses.mouseDeltaYAddr);
78+
});
79+
ev.mouse.x = EM_ASM_INT_V({
80+
return workerApi.getInputValue(workerApi.InputBufferAddresses.mousePositionXAddr);
81+
});
82+
ev.mouse.y = EM_ASM_INT_V({
83+
return workerApi.getInputValue(workerApi.InputBufferAddresses.mousePositionYAddr);
84+
});
85+
86+
// Make sure we don't generate mouse button updates
87+
ev.mouse.button1 = gLastMouseEvent.mouse.button1;
88+
ev.mouse.button2 = gLastMouseEvent.mouse.button2;
89+
ev.mouse.button3 = false;
90+
91+
gMouse->handleEvent(ev);
92+
93+
gLastMouseEvent.mouse.x = ev.mouse.x;
94+
gLastMouseEvent.mouse.y = ev.mouse.y;
95+
}
96+
97+
int hasKeyEvent = EM_ASM_INT_V({
98+
return workerApi.getInputValue(workerApi.InputBufferAddresses.keyEventFlagAddr);
99+
});
100+
if (hasKeyEvent) {
101+
int keyCode = EM_ASM_INT_V({
102+
return workerApi.getInputValue(workerApi.InputBufferAddresses.keyCodeAddr);
103+
});
104+
105+
int keyState = EM_ASM_INT_V({
106+
return workerApi.getInputValue(workerApi.InputBufferAddresses.keyStateAddr);
107+
});
108+
109+
SystemEvent ev;
110+
ev.type = sysevKey;
111+
ev.key.keycode = keyCode;
112+
ev.key.pressed = keyState != 0;
113+
gKeyboard->handleEvent(ev);
114+
}
115+
116+
EM_ASM({ workerApi.releaseInputLock(); });
117+
118+
// Ensure that period tasks are run (until we have idlewait support).
119+
EM_ASM({ workerApi.sleep(0); });
120+
121+
if (gProcessCudaEvents != nullptr) {
122+
gProcessCudaEvents();
123+
}
124+
}
125+
18126
void sys_gui_event() {
127+
sys_gui_poll_events();
19128
static_cast<JSSystemDisplay*>(gDisplay)->blit();
20129
}
21130

22131
void sys_gui_cpu_ops_hook(uint ops) {
23-
// We get invoked every 0x3ffff (256K ops), but for now blit every
132+
sys_gui_poll_events();
133+
134+
// We get invoked every 0x3ffff (256K ops), but for now blit every
24135
// 0x7ffff (512K) ops which is roughly 60 Hz on my machine.
25136
// TODO: use realtime click to actually try to hit 60fps.
26137
if ((ops & 0x7ffff) == 0) {
27138
static_cast<JSSystemDisplay*>(gDisplay)->blit();
28139
}
29140
}
30141

142+
void sys_gui_cuda_hook(SystemEventHandler cudaEventHandler, void (*processCudaEvents)()) {
143+
gKeyboard->attachEventHandler(cudaEventHandler);
144+
gMouse->attachEventHandler(cudaEventHandler);
145+
gProcessCudaEvents = processCudaEvents;
146+
}
147+
31148
void initUI(const char *title, const DisplayCharacteristics &aCharacteristics, int redraw_ms, const KeyboardCharacteristics &keyCharacteristics, bool fullscreen) {
32-
gDisplay = allocSystemDisplay(title, aCharacteristics, redraw_ms);
33-
// gMouse = allocSystemMouse();
34-
// gKeyboard = allocSystemKeyboard();
149+
gDisplay = allocSystemDisplay(title, aCharacteristics, redraw_ms);
150+
gMouse = allocSystemMouse();
151+
gLastMouseEvent.mouse.button1 = false;
152+
gLastMouseEvent.mouse.button2 = false;
153+
gLastMouseEvent.mouse.button3 = false;
154+
gLastMouseEvent.mouse.x = 0;
155+
gLastMouseEvent.mouse.y = 0;
35156

157+
gKeyboard = allocSystemKeyboard();
36158
}
37159

38160
void doneUI() {

src/system/ui/js/sysdisplay.cc

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,17 @@
77
JSSystemDisplay::JSSystemDisplay(
88
const DisplayCharacteristics &chr, int redraw_ms)
99
: SystemDisplay(chr, redraw_ms) {
10-
mClientChar = chr;
10+
mClientChar = chr;
1111
int frameBufferSize =
1212
mClientChar.width * mClientChar.height * mClientChar.bytesPerPixel;
13-
gFrameBuffer = (byte*)malloc(frameBufferSize);
14-
memset(gFrameBuffer, 0, frameBufferSize);
13+
gFrameBuffer = (byte*)malloc(frameBufferSize);
14+
memset(gFrameBuffer, 0, frameBufferSize);
1515

1616
jsFrameBufferSize = mClientChar.width * mClientChar.height * 4;
1717
jsFrameBuffer.reset(new byte[jsFrameBufferSize]);
1818

19-
damageFrameBufferAll();
19+
damageFrameBufferAll();
20+
setMouseGrab(true);
2021

2122
EM_ASM_({ workerApi.didOpenVideo($0, $1); }, mClientChar.width, mClientChar.height);
2223
}
@@ -29,7 +30,7 @@ void JSSystemDisplay::blit() {
2930
}
3031

3132
// TODO: take into account damage area and only blit that part.
32-
healFrameBuffer();
33+
healFrameBuffer();
3334

3435
byte *jsFrameBuffer = this->jsFrameBuffer.get();
3536
int frameBufferSize =
@@ -81,14 +82,14 @@ void JSSystemDisplay::displayShow() {
8182
}
8283

8384
void JSSystemDisplay::convertCharacteristicsToHost(DisplayCharacteristics &aHostChar, const DisplayCharacteristics &aClientChar) {
84-
aHostChar = aClientChar;
85+
aHostChar = aClientChar;
8586
aHostChar.bytesPerPixel = 4;
86-
aHostChar.scanLineLength = aHostChar.bytesPerPixel * aHostChar.width;
87+
aHostChar.scanLineLength = aHostChar.bytesPerPixel * aHostChar.width;
8788
}
8889

8990
bool JSSystemDisplay::changeResolution(const DisplayCharacteristics &aCharacteristics) {
90-
mClientChar = aCharacteristics;
91-
gFrameBuffer = (byte*)realloc(
91+
mClientChar = aCharacteristics;
92+
gFrameBuffer = (byte*)realloc(
9293
gFrameBuffer,
9394
mClientChar.width * mClientChar.height * mClientChar.bytesPerPixel);
9495

@@ -102,14 +103,16 @@ bool JSSystemDisplay::changeResolution(const DisplayCharacteristics &aCharacteri
102103

103104
void JSSystemDisplay::getHostCharacteristics(Container &modes) {
104105
DisplayCharacteristics *hostChar = new DisplayCharacteristics();
105-
convertCharacteristicsToHost(*hostChar, mClientChar);
106+
convertCharacteristicsToHost(*hostChar, mClientChar);
106107
modes.insert(hostChar);
107108
}
108109

109110
void JSSystemDisplay::setMouseGrab(bool enable) {
110-
::printf("JSSystemDisplay::setMouseGrab\n");
111+
// Mouse is always grabbed as far as PearPC is concerned, we just don't sent
112+
// events if it's not.
113+
SystemDisplay::setMouseGrab(true);
111114
}
112115

113116
SystemDisplay *allocSystemDisplay(const char *title, const DisplayCharacteristics &chr, int redraw_ms) {
114-
return new JSSystemDisplay(chr, redraw_ms);
117+
return new JSSystemDisplay(chr, redraw_ms);
115118
}

src/system/ui/js/sysjs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ class JSSystemDisplay: public SystemDisplay {
2323
int jsFrameBufferSize;
2424
};
2525

26+
class SystemKeyboard;
27+
class SystemMouse;
28+
2629
SystemDisplay *allocSystemDisplay(const char *title, const DisplayCharacteristics &chr, int redraw_ms);
30+
SystemKeyboard *allocSystemKeyboard();
31+
SystemMouse *allocSystemMouse();
2732

2833
#endif

src/system/ui/js/syskeyboard.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include "system/keyboard.h"
2+
3+
class JSSystemKeyboard: public SystemKeyboard {
4+
virtual int getKeybLEDs() {
5+
return 0;
6+
}
7+
8+
void setKeybLEDs(int leds) {
9+
}
10+
11+
virtual bool handleEvent(const SystemEvent &ev) {
12+
return SystemKeyboard::handleEvent(ev);
13+
}
14+
};
15+
16+
SystemKeyboard *allocSystemKeyboard() {
17+
if (gKeyboard) return NULL;
18+
return new JSSystemKeyboard();
19+
}

src/system/ui/js/sysmouse.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include "system/mouse.h"
2+
3+
class JSSystemMouse: public SystemMouse {
4+
public:
5+
6+
virtual bool handleEvent(const SystemEvent &ev)
7+
{
8+
return SystemMouse::handleEvent(ev);
9+
}
10+
};
11+
12+
SystemMouse *allocSystemMouse()
13+
{
14+
if (gMouse) return NULL;
15+
return new JSSystemMouse();
16+
}

0 commit comments

Comments
 (0)