[qwaq] Add input test app

The queues in the curses resources struct have been cleaned up and the
threading support code (including for the queues (pipes, really)) has
been moved to its own file.

The input test app currently just prints the devices and the events as
they come in, but demonstrates the new input system working in a
separate thread (though it is currently in with the curses thread).
This commit is contained in:
Bill Currie 2021-09-21 12:58:10 +09:00
parent 106ab86c3e
commit e942fe6528
11 changed files with 863 additions and 288 deletions

View file

@ -2,7 +2,7 @@ QWAQ_LIBS=@QWAQ_LIBS@
QWAQ_DEPS=@QWAQ_DEPS@
QWAQ_INCS=@QWAQ_INCS@
noinst_PROGRAMS += @QWAQ_TARGETS@ ruamoko/qwaq/qwaq-app.dat$(EXEEXT) ruamoko/qwaq/gcd.dat$(EXEEXT) ruamoko/qwaq/z-transform.dat$(EXEEXT)
noinst_PROGRAMS += @QWAQ_TARGETS@ ruamoko/qwaq/qwaq-app.dat$(EXEEXT) ruamoko/qwaq/gcd.dat$(EXEEXT) ruamoko/qwaq/input-app.dat$(EXEEXT) ruamoko/qwaq/z-transform.dat$(EXEEXT)
libui=ruamoko/qwaq/libui.a
noinst_LIBRARIES += $(libui)
@ -52,6 +52,10 @@ qwaq_app_dat_src= \
ruamoko/qwaq/editor/editor.r \
ruamoko/qwaq/editor/status.r
qwaq_input_app_dat_src= \
ruamoko/qwaq/qwaq-input.r \
ruamoko/qwaq/input-app.r
qwaq_curses_libs= \
libs/ui/libQFui.la \
libs/input/libQFinput.la
@ -62,7 +66,8 @@ ruamoko_qwaq_qwaq_curses_SOURCES= \
ruamoko/qwaq/builtins/debug.c \
ruamoko/qwaq/builtins/editbuffer.c \
ruamoko/qwaq/builtins/input.c \
ruamoko/qwaq/builtins/qwaq-curses.c
ruamoko/qwaq/builtins/qwaq-curses.c \
ruamoko/qwaq/builtins/threading.c
ruamoko_qwaq_qwaq_curses_LDADD= $(qwaq_curses_libs) $(QWAQ_LIBS) \
$(PANEL_LIBS) $(NCURSES_LIBS) $(PTHREAD_LDFLAGS) $(DL_LIBS)
@ -116,6 +121,14 @@ ruamoko/qwaq/qwaq-app.dat$(EXEEXT): $(ruamoko_qwaq_qwaq_app_obj) $(QFCC_DEP) $(l
include $(ruamoko_qwaq_qwaq_app_dep) # am--include-marker
r_depfiles_remade += $(ruamoko_qwaq_qwaq_app_dep)
ruamoko_qwaq_input_app_dat_SOURCES=$(qwaq_input_app_dat_src)
ruamoko_qwaq_input_app_obj=$(ruamoko_qwaq_input_app_dat_SOURCES:.r=.o)
ruamoko_qwaq_input_app_dep=$(call qcautodep,$(ruamoko_qwaq_input_app_dat_SOURCES:.o=.Qo))
ruamoko/qwaq/input-app.dat$(EXEEXT): $(ruamoko_qwaq_input_app_obj) $(QFCC_DEP) $(libui) ruamoko/lib/libcsqc.a ruamoko/lib/libr.a
$(V_QFCCLD)$(QLINK) -o $@ $(ruamoko_qwaq_input_app_obj) $(libui) -lcsqc -lr
include $(ruamoko_qwaq_input_app_dep) # am--include-marker
r_depfiles_remade += $(ruamoko_qwaq_input_app_dep)
ruamoko_qwaq_gcd_dat_SOURCES=ruamoko/qwaq/gcd.r
ruamoko_qwaq_gcd_obj=$(ruamoko_qwaq_gcd_dat_SOURCES:.r=.o)
ruamoko_qwaq_gcd_dep=$(call qcautodep,$(ruamoko_qwaq_gcd_dat_SOURCES:.o=.Qo))

File diff suppressed because it is too large Load diff

View file

@ -46,6 +46,7 @@
#include "QF/dstring.h"
#include "QF/hash.h"
#include "QF/in_event.h"
#include "QF/input.h"
#include "QF/keys.h"
#include "QF/sys.h"
@ -188,28 +189,28 @@ qwaq_add_event (qwaq_resources_t *res, qwaq_event_t *event)
int ret = 0;
// merge motion events
pthread_mutex_lock (&res->event_cond.mut);
unsigned last = RB_DATA_AVAILABLE (res->event_queue);
pthread_mutex_lock (&res->events.cond.mut);
unsigned last = RB_DATA_AVAILABLE (res->events.queue);
if (event->what == qe_mousemove && last > 1
&& RB_PEEK_DATA(res->event_queue, last - 1)->what == qe_mousemove) {
RB_POKE_DATA(res->event_queue, last - 1, *event);
&& RB_PEEK_DATA(res->events.queue, last - 1)->what == qe_mousemove) {
RB_POKE_DATA(res->events.queue, last - 1, *event);
merged = 1;
pthread_cond_broadcast (&res->event_cond.rcond);
pthread_cond_broadcast (&res->events.cond.rcond);
}
pthread_mutex_unlock (&res->event_cond.mut);
pthread_mutex_unlock (&res->events.cond.mut);
if (merged) {
return 0;
}
pthread_mutex_lock (&res->event_cond.mut);
pthread_mutex_lock (&res->events.cond.mut);
qwaq_init_timeout (&timeout, 5000 * (int64_t) 1000000);
while (RB_SPACE_AVAILABLE (res->event_queue) < 1 && ret == 0) {
ret = pthread_cond_timedwait (&res->event_cond.wcond,
&res->event_cond.mut, &timeout);
while (RB_SPACE_AVAILABLE (res->events.queue) < 1 && ret == 0) {
ret = pthread_cond_timedwait (&res->events.cond.wcond,
&res->events.cond.mut, &timeout);
}
RB_WRITE_DATA (res->event_queue, event, 1);
pthread_cond_broadcast (&res->event_cond.rcond);
pthread_mutex_unlock (&res->event_cond.mut);
RB_WRITE_DATA (res->events.queue, event, 1);
pthread_cond_broadcast (&res->events.cond.rcond);
pthread_mutex_unlock (&res->events.cond.mut);
return ret;
}
@ -472,9 +473,52 @@ term_register_driver (void)
term_driver_handle = IN_RegisterDriver (&term_driver, 0);
}
static int
qwaq_input_event_handler (const IE_event_t *ie_event, void *_res)
{
qwaq_resources_t *res = _res;
qwaq_event_t event = {};
event.when = ie_event->when * 1e-6 + Sys_DoubleTimeBase ();
switch (ie_event->type) {
case ie_none:
case ie_gain_focus:
case ie_lose_focus:
return 0;
case ie_add_device:
event.what = qe_dev_add;
event.message.int_val = ie_event->device.devid;
break;
case ie_remove_device:
event.what = qe_dev_rem;
event.message.int_val = ie_event->device.devid;
break;
case ie_mouse:
case ie_key:
// these are handled directly for now
break;
case ie_axis:
event.what = qe_axis;
event.message.ivector_val[0] = ie_event->axis.devid;
event.message.ivector_val[1] = ie_event->axis.axis;
event.message.ivector_val[2] = ie_event->axis.value;
break;
case ie_button:
event.what = qe_button;
event.message.ivector_val[0] = ie_event->button.devid;
event.message.ivector_val[1] = ie_event->button.button;
event.message.ivector_val[2] = ie_event->button.state;
break;
}
qwaq_add_event (res, &event);
return 1;
}
void
qwaq_input_init (qwaq_resources_t *res)
{
res->input_event_handler = IE_Add_Handler (qwaq_input_event_handler, res);
IN_DriverData (term_driver_handle, res);
if (res->key_sequences) {
@ -511,6 +555,7 @@ qwaq_process_input (qwaq_resources_t *res)
void
qwaq_input_shutdown (qwaq_resources_t *res)
{
IE_Remove_Handler (res->input_event_handler);
IN_DriverData (term_driver_handle, 0);
// ncurses takes care of input mode for us, so need only tell xterm

View file

@ -0,0 +1,140 @@
/*
threading.c
Ruamoko threading support
Copyright (C) 2020 Bill Currie <bill@taniwha.org>
Copyright (C) 2021 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2020/03/01
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <sys/time.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "QF/dstring.h"
#include "ruamoko/qwaq/qwaq.h"
#include "ruamoko/qwaq/threading.h"
#define always_inline inline __attribute__((__always_inline__))
int
qwaq_pipe_acquire_string (qwaq_pipe_t *pipe)
{
int string_id = -1;
pthread_mutex_lock (&pipe->string_id_cond.mut);
while (RB_DATA_AVAILABLE (pipe->string_ids) < 1) {
pthread_cond_wait (&pipe->string_id_cond.rcond,
&pipe->string_id_cond.mut);
}
RB_READ_DATA (pipe->string_ids, &string_id, 1);
pthread_cond_broadcast (&pipe->string_id_cond.wcond);
pthread_mutex_unlock (&pipe->string_id_cond.mut);
return string_id;
}
void
qwaq_pipe_release_string (qwaq_pipe_t *pipe, int string_id)
{
pthread_mutex_lock (&pipe->string_id_cond.mut);
while (RB_SPACE_AVAILABLE (pipe->string_ids) < 1) {
pthread_cond_wait (&pipe->string_id_cond.wcond,
&pipe->string_id_cond.mut);
}
RB_WRITE_DATA (pipe->string_ids, &string_id, 1);
pthread_cond_broadcast (&pipe->string_id_cond.rcond);
pthread_mutex_unlock (&pipe->string_id_cond.mut);
}
void
qwaq_pipe_submit (qwaq_pipe_t *pipe, const int *cmd, unsigned len)
{
pthread_mutex_lock (&pipe->pipe_cond.mut);
while (RB_SPACE_AVAILABLE (pipe->pipe) < len) {
pthread_cond_wait (&pipe->pipe_cond.wcond,
&pipe->pipe_cond.mut);
}
RB_WRITE_DATA (pipe->pipe, cmd, len);
pthread_cond_broadcast (&pipe->pipe_cond.rcond);
pthread_mutex_unlock (&pipe->pipe_cond.mut);
}
void
qwaq_pipe_receive (qwaq_pipe_t *pipe, int *result, int cmd, unsigned len)
{
Sys_Printf ("qwaq_wait_result: %d %d\n", cmd, len);
pthread_mutex_lock (&pipe->pipe_cond.mut);
while (RB_DATA_AVAILABLE (pipe->pipe) < len
|| *RB_PEEK_DATA (pipe->pipe, 0) != cmd) {
pthread_cond_wait (&pipe->pipe_cond.rcond,
&pipe->pipe_cond.mut);
}
RB_READ_DATA (pipe->pipe, result, len);
pthread_cond_broadcast (&pipe->pipe_cond.wcond);
pthread_mutex_unlock (&pipe->pipe_cond.mut);
Sys_Printf ("qwaq_wait_result exit: %d %d\n", cmd, len);
}
void
qwaq_init_timeout (struct timespec *timeout, int64_t time)
{
#define SEC 1000000000L
struct timeval now;
gettimeofday(&now, 0);
timeout->tv_sec = now.tv_sec;
timeout->tv_nsec = now.tv_usec * 1000L + time;
if (timeout->tv_nsec >= SEC) {
timeout->tv_sec += timeout->tv_nsec / SEC;
timeout->tv_nsec %= SEC;
}
}
void
qwaq_init_cond (rwcond_t *cond)
{
pthread_cond_init (&cond->rcond, 0);
pthread_cond_init (&cond->wcond, 0);
pthread_mutex_init (&cond->mut, 0);
}
void
qwaq_init_pipe (qwaq_pipe_t *pipe)
{
qwaq_init_cond (&pipe->pipe_cond);
qwaq_init_cond (&pipe->string_id_cond);
for (int i = 0; i < STRING_ID_QUEUE_SIZE - 1; i++) {
RB_WRITE_DATA (pipe->string_ids, &i, 1);
pipe->strings[i].mem = &dstring_default_mem;
}
}

35
ruamoko/qwaq/input-app.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef __qwaq_app_h
#define __qwaq_app_h
#include <Object.h>
#include "ruamoko/qwaq/ui/event.h"
#include "ruamoko/qwaq/ui/rect.h"
@class Array;
@class Group;
@class TextContext;
@class View;
extern int color_palette[64];
@interface InputApplication: Object
{
qwaq_event_t event;
qwaq_command endState;
Group *objects;
TextContext *screen;
Extent screenSize;
int autocount;
}
-(Extent)size;
-(TextContext *)screen;
-addView:(View *)view;
-run;
@end
extern InputApplication *application;
#endif//__qwaq_app_h

183
ruamoko/qwaq/input-app.r Normal file
View file

@ -0,0 +1,183 @@
int fence;
#include <AutoreleasePool.h>
#include <key.h>
#include <string.h>
#include "ruamoko/qwaq/ui/color.h"
#include "ruamoko/qwaq/ui/curses.h"
#include "ruamoko/qwaq/ui/group.h"
#include "ruamoko/qwaq/ui/view.h"
#include "ruamoko/qwaq/qwaq-input.h"
#include "ruamoko/qwaq/input-app.h"
string graph_up = " ⢀⢠⢰⢸⡀⣀⣠⣰⣸⡄⣄⣤⣴⣼⡆⣆⣦⣶⣾⡇⣇⣧⣷⣿";
string graph_down = " ⠈⠘⠸⢸⠁⠉⠙⠹⢹⠃⠋⠛⠻⢻⠇⠏⠟⠿⢿⡇⡏⡟⡿⣿";
int color_palette[64];
static AutoreleasePool *autorelease_pool;
static void
arp_start (void)
{
autorelease_pool = [[AutoreleasePool alloc] init];
}
static void
arp_end (void)
{
[autorelease_pool release];
autorelease_pool = nil;
}
@implementation InputApplication
+app
{
return [[[self alloc] init] autorelease];
}
-init
{
if (!(self = [super init])) {
return nil;
}
initialize ();
for (int i = 1; i < 64; i++) {
init_pair (i, i & 0x7, i >> 3);
color_palette[i] = COLOR_PAIR (i);
}
screen = [TextContext screen];
screenSize = [screen size];
objects = [[Group withContext: screen owner: nil] retain];
[screen bkgd: color_palette[047]];
[screen scrollok: 1];
[screen clear];
wrefresh (stdscr);//FIXME
send_connected_devices ();
return self;
}
-(Extent)size
{
return screenSize;
}
-(TextContext *)screen
{
return screen;
}
-draw
{
[objects draw];
[screen refresh];
return self;
}
-handleEvent: (qwaq_event_t *) event
{
switch (event.what) {
case qe_resize:
Extent delta;
delta.width = event.resize.width - screenSize.width;
delta.height = event.resize.height - screenSize.height;
resizeterm (event.resize.width, event.resize.height);
[screen resizeTo: {event.resize.width, event.resize.height}];
screenSize = [screen size];
[objects resize: delta];
[screen refresh];
event.what = qe_none;
break;
case qe_key:
if (event.key.code == '\x18' || event.key.code == '\x11') {
endState = event.message.int_val;
event.what = qe_none;
}
break;
case qe_dev_add:
{
int devid = event.message.int_val;
qwaq_devinfo_t *dev = get_device_info (devid);
[screen printf:"dev add: %d %s %s\n", devid, dev.id, dev.name];
[screen printf:" : %d %d\n", dev.numaxes, dev.numbuttons];
for (int i = 0; i < dev.numaxes; i++) {
[screen printf:" : %d %d %d\n", dev.axes[i].value,
dev.axes[i].min, dev.axes[i].max];
}
[screen refresh];
obj_free (dev.axes);
obj_free (dev.buttons);
str_free (dev.name);
str_free (dev.id);
obj_free (dev);
}
break;
case qe_dev_rem:
[screen printf:"dev rem: %d\n", event.message.int_val];
[screen refresh];
break;
case qe_axis:
[screen printf:"axis: %d %d %d\n", event.message.ivector_val[0],
event.message.ivector_val[1], event.message.ivector_val[2]];
[screen refresh];
break;
case qe_button:
[screen printf:"button: %d %d %d\n", event.message.ivector_val[0],
event.message.ivector_val[1], event.message.ivector_val[2]];
[screen refresh];
}
if (event.what != qe_none) {
[objects handleEvent: event];
}
return self;
}
-run
{
[objects takeFocus];
[self draw];
do {
arp_start ();
get_event (&event);
if (event.what != qe_none) {
[self handleEvent: &event];
}
arp_end ();
} while (!endState);
return self;
}
-addView:(View *)view
{
[objects insertSelected: view];
[screen refresh];
return self;
}
@end
InputApplication *application;
int main (int argc, string *argv)
{
fence = 0;
//while (!fence) {}
arp_start ();
application = [[InputApplication app] retain];
arp_end ();
[application run];
[application release];
qwaq_event_t event;
get_event (&event); // XXX need a "wait for queue idle"
return 0;
}

52
ruamoko/qwaq/qwaq-input.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef __qwaq_input_h
#define __qwaq_input_h
#include "QF/input.h"
typedef struct qwaq_devinfo_s {
int devid;
#ifdef __QFCC__
string name;
string id;
#else
string_t name;
string_t id;
#endif
int numaxes;
#ifdef __QFCC__
in_axisinfo_t *axes;
#else
pointer_t axes;
#endif
int numbuttons;
#ifdef __QFCC__
in_axisinfo_t *buttons;
#else
pointer_t buttons;
#endif
} qwaq_devinfo_t;
#ifdef __QFCC__
#include <Object.h>
#include "ruamoko/qwaq/ui/event.h"
#include "ruamoko/qwaq/ui/rect.h"
@class Array;
@class Group;
@class TextContext;
@class View;
@interface QwaqInput: Object
{
}
@end
void send_connected_devices (void);
qwaq_devinfo_t *get_device_info (int devid);
#endif
#endif//__qwaq_input_h

View file

@ -0,0 +1,4 @@
#include "ruamoko/qwaq/qwaq-input.h"
void send_connected_devices (void) = #0;
qwaq_devinfo_t *get_device_info (int devid) = #0;

39
ruamoko/qwaq/threading.h Normal file
View file

@ -0,0 +1,39 @@
#ifndef __qwaq_threading_h
#define __qwaq_threading_h
#ifndef __QFCC__
#include "QF/dstring.h"
#include "QF/ringbuffer.h"
#define QUEUE_SIZE 16
#define STRING_ID_QUEUE_SIZE 8
#define COMMAND_QUEUE_SIZE 1280
typedef struct rwcond_s {
pthread_cond_t rcond;
pthread_cond_t wcond;
pthread_mutex_t mut;
} rwcond_t;
typedef struct qwaq_pipe_s {
rwcond_t pipe_cond;
RING_BUFFER (int, COMMAND_QUEUE_SIZE) pipe;
rwcond_t string_id_cond;
RING_BUFFER (int, STRING_ID_QUEUE_SIZE + 1) string_ids;
dstring_t strings[STRING_ID_QUEUE_SIZE];
} qwaq_pipe_t;
void qwaq_pipe_submit (qwaq_pipe_t *pipe, const int *data, unsigned len);
void qwaq_pipe_receive (qwaq_pipe_t *pipe, int *data, int id, unsigned len);
int qwaq_pipe_acquire_string (qwaq_pipe_t *pipe);
void qwaq_pipe_release_string (qwaq_pipe_t *pipe, int string_id);
void qwaq_init_pipe (qwaq_pipe_t *pipe);
void qwaq_init_timeout (struct timespec *timeout, int64_t time);
void qwaq_init_cond (rwcond_t *cond);
#endif
#endif//__qwaq_threading_h

View file

@ -145,6 +145,8 @@ void printf(string fmt, ...);
#include "QF/progs.h"
#include "QF/ringbuffer.h"
#include "ruamoko/qwaq/threading.h"
#define QUEUE_SIZE 16
#define STRING_ID_QUEUE_SIZE 8 // must be > 1
#define COMMAND_QUEUE_SIZE 1280
@ -158,12 +160,6 @@ typedef struct panel_s {
int window_id;
} panel_t;
typedef struct rwcond_s {
pthread_cond_t rcond;
pthread_cond_t wcond;
pthread_mutex_t mut;
} rwcond_t;
typedef enum {
esc_ground,
esc_escape,
@ -173,21 +169,22 @@ typedef enum {
esc_key,
} esc_state_t;
typedef struct qwaq_event_queue_s {
rwcond_t cond;
RING_BUFFER (qwaq_event_t, QUEUE_SIZE) queue;
} qwaq_event_queue_t;
typedef struct qwaq_resources_s {
progs_t *pr;
int initialized;
window_t stdscr;
PR_RESMAP (window_t) window_map;
PR_RESMAP (panel_t) panel_map;
rwcond_t event_cond;
RING_BUFFER (qwaq_event_t, QUEUE_SIZE) event_queue;
rwcond_t command_cond;
RING_BUFFER (int, COMMAND_QUEUE_SIZE) command_queue;
rwcond_t results_cond;
RING_BUFFER (int, COMMAND_QUEUE_SIZE) results;
rwcond_t string_id_cond;
RING_BUFFER (int, STRING_ID_QUEUE_SIZE) string_ids;
dstring_t strings[STRING_ID_QUEUE_SIZE - 1];
qwaq_event_queue_t events;
qwaq_pipe_t commands;
qwaq_pipe_t results;
dstring_t escbuff;
esc_state_t escstate;
@ -196,15 +193,15 @@ typedef struct qwaq_resources_s {
int mouse_y;
qwaq_event_t lastClick;
struct hashtab_s *key_sequences;
int input_event_handler;
} qwaq_resources_t;
// gcc stuff
void qwaq_input_init (qwaq_resources_t *res);
void qwaq_input_shutdown (qwaq_resources_t *res);
void qwaq_process_input (qwaq_resources_t *res);
void qwaq_init_timeout (struct timespec *timeout, int64_t time);
int qwaq_add_event (qwaq_resources_t *res, qwaq_event_t *event);
void qwaq_init_cond (rwcond_t *cond);
#endif
#endif//__qwaq_ui_curses_h

View file

@ -22,6 +22,11 @@ typedef enum {
qe_command = 0x0200, // application level command
qe_broadcast = 0x0400,
qe_resize = 0x0800, // screen resized
qe_dev_add = 0x1000,
qe_dev_rem = 0x2000,
qe_axis = 0x4000,
qe_button = 0x8000,
} qwaq_message_event;
typedef enum {
@ -61,6 +66,7 @@ typedef union qwaq_message_s {
int int_val;
float float_val;
float vector_val[4]; // vector and quaternion
int ivector_val[4];
double double_val;
#ifdef __QFCC__
void *pointer_val;