2020-02-26 13:10:59 +00:00
|
|
|
/*
|
|
|
|
#FILENAME#
|
|
|
|
|
|
|
|
#DESCRIPTION#
|
|
|
|
|
|
|
|
Copyright (C) 2001 #AUTHOR#
|
|
|
|
|
|
|
|
Author: #AUTHOR#
|
|
|
|
Date: #DATE#
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2020-02-26 16:18:38 +00:00
|
|
|
#include <curses.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2020-02-27 12:22:10 +00:00
|
|
|
#include <unistd.h>
|
2020-02-26 16:18:38 +00:00
|
|
|
|
|
|
|
#include "QF/dstring.h"
|
2020-02-26 13:10:59 +00:00
|
|
|
#include "QF/progs.h"
|
|
|
|
#include "QF/sys.h"
|
|
|
|
|
|
|
|
#include "qwaq.h"
|
2020-02-27 12:01:09 +00:00
|
|
|
#include "event.h"
|
2020-02-26 13:10:59 +00:00
|
|
|
|
2020-02-26 16:18:38 +00:00
|
|
|
#define always_inline inline __attribute__((__always_inline__))
|
2020-02-27 12:01:09 +00:00
|
|
|
#define QUEUE_SIZE 16 // must be power of 2 greater than 1
|
|
|
|
#define QUEUE_MASK (QUEUE_SIZE - 1)
|
2020-02-27 12:22:10 +00:00
|
|
|
#define MOUSE_MOVES "\033[?1003h" // Make the terminal report mouse movements
|
2020-02-26 16:18:38 +00:00
|
|
|
|
2020-02-28 13:27:29 +00:00
|
|
|
#define RING_BUFFER(type, size) \
|
|
|
|
struct { \
|
|
|
|
type buffer[size]; \
|
|
|
|
unsigned head; \
|
|
|
|
unsigned tail; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define RB_buffer_size(ring_buffer) \
|
|
|
|
({ __auto_type rb = (ring_buffer); \
|
|
|
|
sizeof (rb->buffer) / sizeof (rb->buffer[0]); \
|
|
|
|
})
|
|
|
|
|
|
|
|
#define RB_SPACE_AVAILABLE(ring_buffer) \
|
|
|
|
({ __auto_type rb = &(ring_buffer); \
|
|
|
|
(rb->tail + RB_buffer_size(rb) - rb->head - 1) % RB_buffer_size(rb);\
|
|
|
|
})
|
|
|
|
|
|
|
|
#define RB_DATA_AVAILABLE(ring_buffer) \
|
|
|
|
({ __auto_type rb = &(ring_buffer); \
|
|
|
|
(rb->head + RB_buffer_size (rb) - rb->tail) % RB_buffer_size (rb); \
|
|
|
|
})
|
|
|
|
|
|
|
|
#define RB_WRITE_DATA(ring_buffer, data, count) \
|
|
|
|
({ __auto_type rb = &(ring_buffer); \
|
|
|
|
typeof (&rb->buffer[0]) d = (data); \
|
|
|
|
unsigned c = (count); \
|
|
|
|
unsigned h = rb->head; \
|
|
|
|
rb->head = (h + c) % RB_buffer_size (rb); \
|
|
|
|
if (c > RB_buffer_size (rb) - h) { \
|
|
|
|
memcpy (rb->buffer + h, d, \
|
|
|
|
(RB_buffer_size (rb) - h) * sizeof (rb->buffer[0])); \
|
|
|
|
c -= RB_buffer_size (rb) - h; \
|
|
|
|
d += RB_buffer_size (rb) - h; \
|
|
|
|
h = 0; \
|
|
|
|
} \
|
|
|
|
memcpy (rb->buffer + h, d, c * sizeof (rb->buffer[0])); \
|
|
|
|
})
|
|
|
|
|
|
|
|
#define RB_READ_DATA(ring_buffer, data, count) \
|
|
|
|
({ __auto_type rb = &(ring_buffer); \
|
|
|
|
typeof (&rb->buffer[0]) d = (data); \
|
|
|
|
unsigned c = (count); \
|
|
|
|
unsigned oc = c; \
|
|
|
|
unsigned t = rb->tail; \
|
|
|
|
if (c > RB_buffer_size (rb) - t) { \
|
|
|
|
memcpy (d, rb->buffer + t, \
|
|
|
|
(RB_buffer_size (rb) - t) * sizeof (rb->buffer[0])); \
|
|
|
|
c -= RB_buffer_size (rb) - t; \
|
|
|
|
d += RB_buffer_size (rb) - t; \
|
|
|
|
t = 0; \
|
|
|
|
} \
|
|
|
|
memcpy (d, rb->buffer + t, c * sizeof (rb->buffer[0])); \
|
|
|
|
rb->tail = (t + oc) % RB_buffer_size (rb); \
|
|
|
|
})
|
|
|
|
|
2020-02-26 16:18:38 +00:00
|
|
|
typedef struct window_s {
|
|
|
|
WINDOW *win;
|
|
|
|
} window_t;
|
|
|
|
|
|
|
|
typedef struct qwaq_resources_s {
|
|
|
|
progs_t *pr;
|
|
|
|
int initialized;
|
|
|
|
dstring_t *print_buffer;
|
|
|
|
PR_RESMAP (window_t) window_map;
|
2020-02-28 13:27:29 +00:00
|
|
|
RING_BUFFER (qwaq_event_t, QUEUE_SIZE) event_queue;
|
2020-02-26 16:18:38 +00:00
|
|
|
} qwaq_resources_t;
|
|
|
|
|
|
|
|
static window_t *
|
|
|
|
window_new (qwaq_resources_t *res)
|
|
|
|
{
|
|
|
|
PR_RESNEW (window_t, res->window_map);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
window_free (qwaq_resources_t *res, window_t *win)
|
|
|
|
{
|
|
|
|
PR_RESFREE (window_t, res->window_map, win);
|
|
|
|
}
|
2020-02-26 13:10:59 +00:00
|
|
|
|
2020-02-26 16:18:38 +00:00
|
|
|
static void
|
|
|
|
window_reset (qwaq_resources_t *res)
|
|
|
|
{
|
|
|
|
PR_RESRESET (window_t, res->window_map);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline window_t *
|
|
|
|
window_get (qwaq_resources_t *res, unsigned index)
|
|
|
|
{
|
|
|
|
PR_RESGET(res->window_map, index);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
window_index (qwaq_resources_t *res, window_t *win)
|
|
|
|
{
|
|
|
|
PR_RESINDEX (res->window_map, win);
|
|
|
|
}
|
|
|
|
|
2020-02-28 13:27:29 +00:00
|
|
|
static always_inline window_t * __attribute__((pure))
|
2020-02-26 16:18:38 +00:00
|
|
|
get_window (qwaq_resources_t *res, const char *name, int handle)
|
|
|
|
{
|
|
|
|
window_t *window = window_get (res, handle);
|
|
|
|
|
|
|
|
if (!window || !window->win) {
|
|
|
|
PR_RunError (res->pr, "invalid window passed to %s", name + 3);
|
|
|
|
}
|
|
|
|
return window;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int need_endwin;
|
2020-02-26 13:10:59 +00:00
|
|
|
static void
|
|
|
|
bi_shutdown (void)
|
|
|
|
{
|
2020-02-26 16:18:38 +00:00
|
|
|
if (need_endwin) {
|
|
|
|
endwin ();
|
|
|
|
}
|
2020-02-26 13:10:59 +00:00
|
|
|
}
|
|
|
|
|
2020-02-26 16:18:38 +00:00
|
|
|
static void
|
|
|
|
bi_initialize (progs_t *pr)
|
|
|
|
{
|
|
|
|
qwaq_resources_t *res = PR_Resources_Find (pr, "qwaq");
|
|
|
|
|
|
|
|
initscr ();
|
|
|
|
need_endwin = 1;
|
|
|
|
res->initialized = 1;
|
|
|
|
raw ();
|
|
|
|
keypad (stdscr, TRUE);
|
|
|
|
noecho ();
|
|
|
|
nonl ();
|
2020-02-27 12:01:09 +00:00
|
|
|
nodelay (stdscr, TRUE);
|
2020-02-27 12:22:10 +00:00
|
|
|
mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL);
|
|
|
|
write(1, MOUSE_MOVES, sizeof (MOUSE_MOVES) - 1);
|
2020-02-27 12:38:55 +00:00
|
|
|
refresh();
|
2020-02-26 16:18:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bi_create_window (progs_t *pr)
|
|
|
|
{
|
|
|
|
qwaq_resources_t *res = PR_Resources_Find (pr, "qwaq");
|
|
|
|
int xpos = P_INT (pr, 0);
|
|
|
|
int ypos = P_INT (pr, 1);
|
|
|
|
int xlen = P_INT (pr, 2);
|
|
|
|
int ylen = P_INT (pr, 3);
|
|
|
|
window_t *window = window_new (res);
|
|
|
|
window->win = newwin (ylen, xlen, ypos, xpos);
|
|
|
|
keypad (window->win, TRUE);
|
|
|
|
R_INT (pr) = window_index (res, window);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bi_destroy_window (progs_t *pr)
|
|
|
|
{
|
|
|
|
qwaq_resources_t *res = PR_Resources_Find (pr, "qwaq");
|
|
|
|
window_t *window = get_window (res, __FUNCTION__, P_INT (pr, 0));
|
|
|
|
delwin (window->win);
|
|
|
|
window->win = 0;
|
|
|
|
window_free (res, window);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bi_wprintf (progs_t *pr)
|
|
|
|
{
|
|
|
|
qwaq_resources_t *res = PR_Resources_Find (pr, "qwaq");
|
|
|
|
window_t *window = get_window (res, __FUNCTION__, P_INT (pr, 0));
|
|
|
|
const char *fmt = P_GSTRING (pr, 1);
|
|
|
|
int count = pr->pr_argc - 2;
|
|
|
|
pr_type_t **args = pr->pr_params + 2;
|
|
|
|
|
2020-02-26 17:11:54 +00:00
|
|
|
dstring_clearstr (res->print_buffer);
|
2020-02-26 16:18:38 +00:00
|
|
|
PR_Sprintf (pr, res->print_buffer, "bi_wprintf", fmt, count, args);
|
|
|
|
waddstr (window->win, res->print_buffer->str);
|
|
|
|
wrefresh (window->win);
|
|
|
|
}
|
|
|
|
|
2020-02-27 12:22:10 +00:00
|
|
|
static void
|
|
|
|
bi_mvwprintf (progs_t *pr)
|
|
|
|
{
|
|
|
|
qwaq_resources_t *res = PR_Resources_Find (pr, "qwaq");
|
|
|
|
window_t *window = get_window (res, __FUNCTION__, P_INT (pr, 0));
|
|
|
|
int x = P_INT (pr, 1);
|
|
|
|
int y = P_INT (pr, 2);
|
|
|
|
const char *fmt = P_GSTRING (pr, 3);
|
|
|
|
int count = pr->pr_argc - 4;
|
|
|
|
pr_type_t **args = pr->pr_params + 4;
|
|
|
|
|
|
|
|
dstring_clearstr (res->print_buffer);
|
|
|
|
PR_Sprintf (pr, res->print_buffer, "bi_wprintf", fmt, count, args);
|
|
|
|
mvwaddstr (window->win, y, x, res->print_buffer->str);
|
|
|
|
wrefresh (window->win);
|
|
|
|
}
|
|
|
|
|
2020-02-26 16:18:38 +00:00
|
|
|
static void
|
|
|
|
bi_wgetch (progs_t *pr)
|
|
|
|
{
|
|
|
|
qwaq_resources_t *res = PR_Resources_Find (pr, "qwaq");
|
|
|
|
window_t *window = get_window (res, __FUNCTION__, P_INT (pr, 0));
|
|
|
|
|
|
|
|
R_INT (pr) = wgetch (window->win);
|
|
|
|
}
|
|
|
|
|
2020-02-27 12:01:09 +00:00
|
|
|
static void
|
|
|
|
add_event (qwaq_resources_t *res, qwaq_event_t *event)
|
|
|
|
{
|
2020-02-28 13:27:29 +00:00
|
|
|
if (RB_SPACE_AVAILABLE (res->event_queue) >= 1) {
|
|
|
|
RB_WRITE_DATA (res->event_queue, event, 1);
|
2020-02-27 12:01:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
get_event (qwaq_resources_t *res, qwaq_event_t *event)
|
|
|
|
{
|
2020-02-28 13:27:29 +00:00
|
|
|
if (RB_DATA_AVAILABLE (res->event_queue) >= 1) {
|
2020-02-27 12:01:09 +00:00
|
|
|
if (event) {
|
2020-02-28 13:27:29 +00:00
|
|
|
RB_READ_DATA (res->event_queue, event, 1);
|
2020-02-27 12:01:09 +00:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
mouse_event (qwaq_resources_t *res, MEVENT *mevent)
|
|
|
|
{
|
|
|
|
qwaq_event_t event = {};
|
|
|
|
event.event_type = qe_mouse;
|
|
|
|
event.e.mouse.x = mevent->x;
|
|
|
|
event.e.mouse.y = mevent->y;
|
|
|
|
event.e.mouse.buttons = mevent->bstate;
|
|
|
|
add_event (res, &event);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
key_event (qwaq_resources_t *res, int key)
|
|
|
|
{
|
|
|
|
qwaq_event_t event = {};
|
|
|
|
event.event_type = qe_key;
|
|
|
|
event.e.key = key;
|
|
|
|
add_event (res, &event);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bi_process_input (progs_t *pr)
|
|
|
|
{
|
|
|
|
qwaq_resources_t *res = PR_Resources_Find (pr, "qwaq");
|
|
|
|
if (Sys_CheckInput (1, -1)) {
|
|
|
|
int ch;
|
|
|
|
while ((ch = getch ()) != ERR && ch) {
|
|
|
|
fflush (stderr);
|
|
|
|
if (ch == KEY_MOUSE) {
|
|
|
|
MEVENT mevent;
|
|
|
|
getmouse (&mevent);
|
|
|
|
mouse_event (res, &mevent);
|
|
|
|
} else {
|
|
|
|
key_event (res, ch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bi_get_event (progs_t *pr)
|
|
|
|
{
|
|
|
|
qwaq_resources_t *res = PR_Resources_Find (pr, "qwaq");
|
|
|
|
qwaq_event_t *event = &G_STRUCT (pr, qwaq_event_t, P_INT (pr, 0));
|
|
|
|
|
|
|
|
R_INT (pr) = get_event (res, event);
|
|
|
|
}
|
|
|
|
|
2020-02-26 16:18:38 +00:00
|
|
|
static void
|
|
|
|
bi_qwaq_clear (progs_t *pr, void *data)
|
|
|
|
{
|
|
|
|
__auto_type res = (qwaq_resources_t *) data;
|
|
|
|
|
|
|
|
if (res->initialized) {
|
|
|
|
endwin ();
|
|
|
|
}
|
|
|
|
need_endwin = 0;
|
|
|
|
window_reset (res);
|
|
|
|
}
|
|
|
|
|
|
|
|
static builtin_t builtins[] = {
|
|
|
|
{"initialize", bi_initialize, -1},
|
|
|
|
{"create_window", bi_create_window, -1},
|
|
|
|
{"destroy_window", bi_destroy_window, -1},
|
|
|
|
{"wprintf", bi_wprintf, -1},
|
2020-02-27 12:22:10 +00:00
|
|
|
{"mvwprintf", bi_mvwprintf, -1},
|
2020-02-26 16:18:38 +00:00
|
|
|
{"wgetch", bi_wgetch, -1},
|
2020-02-27 12:01:09 +00:00
|
|
|
{"process_input", bi_process_input, -1},
|
|
|
|
{"get_event", bi_get_event, -1},
|
2020-02-26 16:18:38 +00:00
|
|
|
{0}
|
|
|
|
};
|
|
|
|
|
2020-02-26 13:10:59 +00:00
|
|
|
void
|
|
|
|
BI_Init (progs_t *pr)
|
|
|
|
{
|
2020-02-26 16:18:38 +00:00
|
|
|
qwaq_resources_t *res = calloc (sizeof (*res), 1);
|
|
|
|
res->pr = pr;
|
|
|
|
res->print_buffer = dstring_newstr ();
|
2020-02-26 13:10:59 +00:00
|
|
|
|
2020-02-26 16:18:38 +00:00
|
|
|
PR_Resources_Register (pr, "qwaq", res, bi_qwaq_clear);
|
|
|
|
PR_RegisterBuiltins (pr, builtins);
|
2020-02-26 13:10:59 +00:00
|
|
|
Sys_RegisterShutdown (bi_shutdown);
|
|
|
|
}
|