toaruos/userspace/lib/window.c
Kevin Lange 4c9be9361a Compositor animations - actual
There are both some good changes and some terribly hacky changes in this
commit.

- Server uses a different struct than client now for windows, so
  arbitrary compositor things like effect parameters can be added
  without having to rebuild everything. That's good.
- Client window structs no longer attempt to track or use window
  locations. That's good.
- Windows do not destroy in response to the destory command, but rather,
  a special animation is triggered; the end state for this animation
  calls the actually window destruction code. That's a hack.
- Animations are based on rendered frames, not on time. That's a hack.
2013-05-29 21:31:59 -07:00

548 lines
14 KiB
C

/* vim: tabstop=4 shiftwidth=4 noexpandtab
*
* Window Library
*/
#include <syscall.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <sys/stat.h>
#include <signal.h>
#include "window.h"
#include "pthread.h"
extern FILE *fdopen(int fildes, const char *mode);
static int event_pipe;
static int mouse_event_pipe;
static int gobble_mouse_events = 1;
#define LOCK(lock) while (__sync_lock_test_and_set(&lock, 0x01)) { syscall_yield(); };
#define UNLOCK(lock) __sync_lock_release(&lock);
#define WIN_B 4
volatile wins_server_global_t * wins_globals = NULL;
process_windows_t * process_windows = NULL;
#define get_window wins_get_window
window_t * wins_get_window (wid_t wid) {
foreach(n, process_windows->windows) {
window_t * w = (window_t *)n->value;
if (w->wid == wid) {
return w;
}
}
return NULL;
}
void (*mouse_action_callback)(w_mouse_t *) = NULL;
void (*resize_window_callback)(window_t *) = NULL;
void (*focus_changed_callback)(window_t *) = NULL;
/* Window Object Management */
/*XXX ... */
window_t * init_window_client (process_windows_t * pw, wid_t wid, int32_t x, int32_t y, uint16_t width, uint16_t height, uint16_t index) {
window_t * window = malloc(sizeof(window_t));
if (!window) {
fprintf(stderr, "[%d] [window] Could not malloc a window_t!", getpid());
return NULL;
}
window->owner = pw;
window->wid = wid;
window->bufid = 0;
window->width = width;
window->height = height;
window->focused = 0;
char key[1024];
SHMKEY_(key, 1024, window);
size_t size = (width * height * WIN_B);
window->buffer = (uint8_t *)syscall_shm_obtain(key, &size);
if (!window->buffer) {
fprintf(stderr, "[%d] [window] Could not create a buffer for a new window for pid %d!", getpid(), pw->pid);
free(window);
return NULL;
}
list_insert(pw->windows, window);
return window;
}
void free_window_client (window_t * window) {
/* Free the window buffer */
if (!window) return;
char key[256];
SHMKEY(key, 256, window);
syscall_shm_release(key);
/* Now, kill the object itself */
process_windows_t * pw = window->owner;
node_t * n = list_find(pw->windows, window);
if (n) {
list_delete(pw->windows, n);
free(n);
}
}
void resize_window_buffer_client (window_t * window, int16_t left, int16_t top, uint16_t width, uint16_t height) {
if (!window) {
return;
}
/* If the window has enlarged, we need to create a new buffer */
if ((width * height) > (window->width * window->height)) {
/* Release the old buffer */
char key[256], keyn[256];
SHMKEY_(key, 256, window);
/* Create the new one */
window->bufid++;
SHMKEY_(keyn, 256, window);
size_t size = (width * height * WIN_B);
char * new_buffer = (uint8_t *)syscall_shm_obtain(keyn, &size);
window->buffer = new_buffer;
syscall_shm_release(key);
}
window->width = width;
window->height = height;
}
/* Command Dispatch */
uint8_t volatile wins_command_lock;
uint8_t volatile wins_command_recvd;
window_t volatile * wins_last_new;
void wins_send_command (wid_t wid, int16_t left, int16_t top, uint16_t width, uint16_t height, int command, int wait_for_reply) {
/* Construct the header and packet */
wins_packet_t header;
header.magic = WINS_MAGIC;
header.command_type = command;
header.packet_size = sizeof(w_window_t);
w_window_t packet;
packet.wid = wid;
packet.left = left;
packet.top = top;
packet.width = width;
packet.height = height;
/* Send them */
LOCK(wins_command_lock);
wins_command_recvd = 0xFF; // XXX: Will this work?
fwrite(&header, sizeof(wins_packet_t), 1, process_windows->command_pipe_file);
fwrite(&packet, sizeof(w_window_t), 1, process_windows->command_pipe_file);
fflush(process_windows->command_pipe_file);
/* Now wait for the command to be processed before returning */
if (wait_for_reply) {
syscall_send_signal(process_windows->pid, SIGWINEVENT);
while((wins_command_recvd & 0xF) != (command & 0xF)) {
syscall_yield();
}
}
UNLOCK(wins_command_lock);
}
window_t * window_create (int16_t left, int16_t top, uint16_t width, uint16_t height) {
wins_send_command(0, left, top, width, height, WC_NEWWINDOW, 1);
while (!wins_last_new) {
syscall_yield();
}
return (window_t *)wins_last_new;
}
void window_resize (window_t * window, int16_t left, int16_t top, uint16_t width, uint16_t height) {
wins_send_command(window->wid, left, top, width, height, WC_RESIZE, 1);
}
void window_redraw (window_t * window, int16_t left, int16_t top, uint16_t width, uint16_t height) {
wins_send_command(window->wid, left, top, width, height, WC_DAMAGE, 0);
}
void window_redraw_full (window_t * window) {
wins_send_command(window->wid, 0, 0, window->width, window->height, WC_DAMAGE, 0);
}
void window_redraw_wait (window_t * window) {
wins_send_command(window->wid, 0, 0, window->width, window->height, WC_REDRAW, 1);
}
void window_destroy (window_t * window) {
wins_send_command(window->wid, 0, 0, 0, 0, WC_DESTROY, 1);
free_window_client(window);
}
void window_reorder (window_t * window, uint16_t new_zed) {
wins_send_command(window->wid, new_zed, 0, 0, 0, WC_REORDER, 0);
}
void window_enable_alpha (window_t * window) {
wins_send_command(window->wid, 1, 0, 0, 0, WC_SET_ALPHA, 0);
}
void window_disable_alpha (window_t * window) {
wins_send_command(window->wid, 0, 0, 0, 0, WC_SET_ALPHA, 0);
}
/* Event Processing (invoked by signal only) */
#define MAX_UNREAD_KEY_EVENTS 200
#define MAX_UNREAD_MOUSE_EVENTS 200
static int kbd_read = 0;
static int mouse_read = 0;
w_keyboard_t * poll_keyboard () {
w_keyboard_t * evt = NULL;
read(event_pipe, &evt, sizeof(evt));
kbd_read--;
return evt;
}
w_keyboard_t * poll_keyboard_async() {
w_keyboard_t * evt = NULL;
struct stat _stat;
fstat(event_pipe, &_stat);
if (_stat.st_size >= sizeof(evt)) {
read(event_pipe, &evt, sizeof(evt));
kbd_read--;
}
return evt;
}
static void process_key_evt (uint8_t command, w_keyboard_t * evt) {
/* Push the event onto a buffer for the process to poll */
kbd_read++;
if (kbd_read > MAX_UNREAD_KEY_EVENTS) {
char garbage[sizeof(evt)];
read(event_pipe, &garbage, sizeof(evt));
kbd_read--;
}
write(event_pipe, &evt, sizeof(evt));
}
w_mouse_t * poll_mouse () {
w_mouse_t * evt = NULL;
read(mouse_event_pipe, &evt, sizeof(evt));
mouse_read--;
return evt;
}
static void process_mouse_evt (uint8_t command, w_mouse_t * evt) {
/* Push the event onto a buffer for the process to poll */
if (gobble_mouse_events) return; /* om nom nom */
mouse_read++;
if (mouse_read > MAX_UNREAD_MOUSE_EVENTS) {
char garbage[sizeof(evt)];
read(mouse_event_pipe, &garbage, sizeof(evt));
mouse_read--;
}
if (mouse_action_callback) {
mouse_action_callback(evt);
}
write(mouse_event_pipe, &evt, sizeof(evt));
}
static void process_window_evt (uint8_t command, w_window_t evt) {
switch (command) {
window_t * window = NULL;
case WE_NEWWINDOW:
window = init_window_client(process_windows, evt.wid, evt.left, evt.top, evt.width, evt.height, 0);
wins_last_new = window;
break;
case WE_FOCUSCHG:
window = get_window(evt.wid);
if (window) {
window->focused = evt.left;
if (focus_changed_callback) {
focus_changed_callback(window);
}
}
break;
case WE_RESIZED:
/* XXX: We need a lock or something to contend the window buffer */
window = get_window(evt.wid);
if (!window) {
fprintf(stderr, "[%d] [window] SEVERE: wins sent WE_RESIZED for window we don't have!\n", getpid());
return;
}
resize_window_buffer_client(window, evt.left, evt.top, evt.width, evt.height);
if (resize_window_callback) {
resize_window_callback(window);
}
break;
}
wins_command_recvd = command;
}
static void process_evt () {
/* Wait for and process one event */
wins_packet_t header;
read(process_windows->event_pipe, &header, sizeof(wins_packet_t));
while (header.magic != WINS_MAGIC) {
/* REALIGN!! */
memcpy(&header, (void *)((uintptr_t)&header + 1), (sizeof(header) - 1));
read(process_windows->event_pipe, (char *)((uintptr_t)&header + sizeof(header) - 1), 1);
}
/* Determine type, read, and dispatch */
switch (header.command_type & WE_GROUP_MASK) {
case WE_MOUSE_EVT: {
w_mouse_t * mevt = malloc(sizeof(w_mouse_t));
read(process_windows->event_pipe, mevt, sizeof(w_mouse_t));
process_mouse_evt(header.command_type, mevt);
break;
}
case WE_KEY_EVT: {
w_keyboard_t * kevt = malloc(sizeof(w_keyboard_t));
read(process_windows->event_pipe, kevt, sizeof(w_keyboard_t));
process_key_evt(header.command_type, kevt);
break;
}
case WE_WINDOW_EVT: {
w_window_t wevt;
read(process_windows->event_pipe, &wevt, sizeof(w_window_t));
process_window_evt(header.command_type, wevt);
break;
}
default:
fprintf(stderr, "[%d] [window] WARN: Received unknown event type %d, 0x%x\n", getpid(), header.command_type, header.packet_size);
struct stat buf;
fstat(process_windows->event_pipe, &buf);
char devnull[1];
for (uint32_t i = 0; i < buf.st_size; ++i) {
read(process_windows->event_pipe, devnull, 1);
}
break;
}
}
static void sig_process_evt(int sig) {
if (!process_windows) return;
struct stat buf;
fstat(process_windows->event_pipe, &buf);
do {
process_evt();
fstat(process_windows->event_pipe, &buf);
} while (buf.st_size);
wins_packet_t header;
}
void install_signal_handlers () {
syscall_signal(SIGWINEVENT, sig_process_evt); // SIGWINEVENT
}
static void ignore(int sig) {
return;
}
void * win_threaded_event_processor(void * garbage) {
while (1) {
process_evt();
}
}
static int _disabled_sigwinevent = 0;
void win_use_threaded_handler() {
_disabled_sigwinevent = 1;
syscall_signal(SIGWINEVENT, ignore); // SIGWINEVENT
pthread_t event_thread;
pthread_create(&event_thread, NULL, win_threaded_event_processor, NULL);
gobble_mouse_events = 0;
}
void win_sane_events() {
_disabled_sigwinevent = 1;
syscall_signal(SIGWINEVENT, ignore); // SIGWINEVENT
gobble_mouse_events = 0;
}
wins_packet_t * get_window_events() {
/* Wait for and return one window event; process the core window events, but also return them for reference */
wins_packet_t header;
read(process_windows->event_pipe, &header, sizeof(wins_packet_t));
while (header.magic != WINS_MAGIC) {
/* REALIGN!! */
memcpy(&header, (void *)((uintptr_t)&header + 1), (sizeof(header) - 1));
read(process_windows->event_pipe, (char *)((uintptr_t)&header + sizeof(header) - 1), 1);
}
wins_packet_t * out = malloc(sizeof(wins_packet_t) + header.packet_size);
memcpy(out, &header, sizeof(wins_packet_t));
read(process_windows->event_pipe, (char *)((uintptr_t)out + sizeof(wins_packet_t)), header.packet_size);
return out;
}
wins_packet_t * get_window_events_async() {
struct stat _stat;
fstat(process_windows->event_pipe, &_stat);
if (_stat.st_size >= sizeof(wins_packet_t)) {
return get_window_events();
} else {
return NULL;
}
}
/* Initial Connection */
int wins_connect() {
if (wins_globals) {
/* Already connected. Bailing. */
return 0;
}
size_t size = sizeof(wins_server_global_t);
wins_globals = (volatile wins_server_global_t *)syscall_shm_obtain(WINS_SERVER_IDENTIFIER, &size);
if (!wins_globals) {
fprintf(stderr, "[%d] [window] Unable to connect with wins through shared memory.\n", getpid());
return EACCES;
}
/* Verify magic */
if (wins_globals->magic != WINS_MAGIC) {
/* If the magic is incorrent, this probably means the server isn't available. */
fprintf(stderr, "[%d] [window] Window server [%p] size claims to be %dx%d\n", getpid(), wins_globals, wins_globals->server_width, wins_globals->server_height);
fprintf(stderr, "[%d] [window] Window server not available (expected magic %x, got %x)\n", getpid(), WINS_MAGIC, wins_globals->magic);
syscall_shm_release(WINS_SERVER_IDENTIFIER);
return EAGAIN;
}
/* Enter handshake lock */
LOCK(wins_globals->lock);
/* Lock Obtained */
/* Share client PID */
wins_globals->client_pid = getpid();
wins_globals->server_done = 0;
/* Mark us as done and wait for the server */
wins_globals->client_done = 1;
while (!wins_globals->server_done) {
syscall_yield();
}
assert(process_windows && "process_windows was not initialized!");
process_windows->pid = wins_globals->server_pid;
process_windows->event_pipe = syscall_get_fd(wins_globals->event_pipe);
process_windows->command_pipe = syscall_get_fd(wins_globals->command_pipe);
process_windows->command_pipe_file = fdopen(process_windows->command_pipe, "w");
if (process_windows->event_pipe < 0) {
fprintf(stderr, "ERROR: Failed to initialize an event pipe!\n");
return 1;
}
/* Reset client status for next client */
wins_globals->client_done = 0;
wins_globals->event_pipe = 0;
wins_globals->command_pipe = 0;
wins_globals->client_pid = 0;
wins_globals->server_done = 0;
/* Done with lock */
UNLOCK(wins_globals->lock);
return 0;
}
int wins_disconnect() {
#if 0
syscall_shm_release(WINS_SERVER_IDENTIFIER);
#endif
if (wins_globals) {
//free((wins_server_global_t *)wins_globals);
wins_globals = NULL;
}
}
/* Client Setup/Teardown */
int setup_windowing () {
if (!process_windows) {
process_windows = malloc(sizeof(process_windows_t));
process_windows->windows = list_create();
}
event_pipe = syscall_mkpipe();
mouse_event_pipe = syscall_mkpipe();
install_signal_handlers();
return wins_connect();
}
void teardown_windowing () {
if (process_windows) {
if (_disabled_sigwinevent) {
syscall_signal(SIGWINEVENT, sig_process_evt); // SIGWINEVENT
}
window_t * window;
node_t * node;
while ((node = list_pop(process_windows->windows)) != NULL) {
window = node->value;
if (!window) break;
window_destroy(window);
}
free(process_windows->windows);
free(process_windows);
process_windows = NULL;
}
wins_disconnect();
}