toaruos/userspace/lib/window.c

591 lines
15 KiB
C
Raw Normal View History

2012-02-12 04:54:34 +04:00
/* 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>
2012-02-13 03:00:21 +04:00
#include "../../kernel/include/signal.h"
2012-02-12 04:54:34 +04:00
#include "window.h"
#include "pthread.h"
2012-02-12 04:54:34 +04:00
FILE *fdopen(int fildes, const char *mode);
2012-03-02 07:13:52 +04:00
#define LOCK(lock) while (__sync_lock_test_and_set(&lock, 0x01)) { syscall_yield(); };
2012-02-12 04:54:34 +04:00
#define UNLOCK(lock) __sync_lock_release(&lock);
2012-02-13 02:45:23 +04:00
#define WIN_B 4
2012-02-12 04:54:34 +04:00
volatile wins_server_global_t * wins_globals = NULL;
process_windows_t * process_windows = NULL;
static window_t * 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;
}
2012-10-14 00:02:58 +04:00
void (*mouse_action_callback)(w_mouse_t *) = NULL;
void (*resize_window_callback)(window_t *) = NULL;
2012-02-12 04:54:34 +04:00
/* Window Object Management */
window_t * init_window (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->x = x;
window->y = y;
window->z = index;
window->use_alpha = 0;
2012-02-12 04:54:34 +04:00
2012-02-13 02:45:23 +04:00
char key[1024];
SHMKEY(key, 1024, window);
2012-02-13 05:30:36 +04:00
size_t size = (width * height * WIN_B);
window->buffer = (uint8_t *)syscall_shm_obtain(key, &size);
2012-02-13 05:30:36 +04:00
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;
}
/*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->x = x;
window->y = y;
window->z = index;
char key[1024];
SHMKEY_(key, 1024, window);
2012-02-13 02:45:23 +04:00
2012-10-14 00:02:58 +04:00
fprintf(stderr, "Optaining SHMEM region at %s\n", key);
size_t size = (width * height * WIN_B);
window->buffer = (uint8_t *)syscall_shm_obtain(key, &size);
2012-02-13 02:45:23 +04:00
2012-02-12 04:54:34 +04:00
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) {
2012-02-12 04:54:34 +04:00
/* Free the window buffer */
2012-02-26 08:47:20 +04:00
if (!window) return;
2012-02-12 04:54:34 +04:00
char key[256];
SHMKEY(key, 256, window);
syscall_shm_release(key);
/* Now, kill the object itself */
process_windows_t * pw = window->owner;
2012-03-08 09:44:02 +04:00
node_t * n = list_find(pw->windows, window);
if (n) {
list_delete(pw->windows, n);
free(n);
2012-03-08 09:44:02 +04:00
}
}
void free_window (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;
2012-02-12 04:54:34 +04:00
node_t * n = list_find(pw->windows, window);
2012-03-02 07:13:52 +04:00
if (n) {
list_delete(pw->windows, n);
free(n);
}
2012-02-12 04:54:34 +04:00
}
void resize_window_buffer (window_t * window, int16_t left, int16_t top, uint16_t width, uint16_t height) {
2012-02-12 04:54:34 +04:00
2012-02-13 05:30:36 +04:00
if (!window) {
return;
}
2012-02-12 04:54:34 +04:00
/* If the window has enlarged, we need to create a new buffer */
if ((width * height) > (window->width * window->height)) {
/* Release the old buffer */
2012-03-02 07:13:52 +04:00
char key[256], keyn[256];
2012-02-12 04:54:34 +04:00
SHMKEY(key, 256, window);
2012-03-02 07:13:52 +04:00
2012-10-14 00:02:58 +04:00
printf("Current window buffer is %s\n", key);
2012-02-12 04:54:34 +04:00
/* Create the new one */
window->bufid++;
2012-03-02 07:13:52 +04:00
SHMKEY(keyn, 256, window);
2012-10-14 00:02:58 +04:00
printf("New window buffer will be %s\n", keyn);
size_t size = (width * height * WIN_B);
printf("Required size for new buffer is %d\n", size);
printf("Obtaining... \n");
char * new_buffer = (uint8_t *)syscall_shm_obtain(keyn, &size);
printf("Clearing to zeros...\n");
memset(new_buffer, 0x44, size);
printf("Redirecting compositor-side buffer to new buffer.\n");
window->buffer = new_buffer;
//syscall_shm_release(key);
}
window->x = left;
window->y = top;
window->width = width;
window->height = height;
}
void resize_window_buffer_client (window_t * window, int16_t left, int16_t top, uint16_t width, uint16_t height) {
2012-03-02 07:13:52 +04:00
2012-10-14 00:02:58 +04:00
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);
printf("Current window buffer is %s\n", key);
/* Create the new one */
window->bufid++;
SHMKEY_(keyn, 256, window);
printf("New window buffer will be %s\n", keyn);
2012-03-02 07:13:52 +04:00
size_t size = (width * height * WIN_B);
2012-10-14 00:02:58 +04:00
printf("Required size for new buffer is %d\n", size);
printf("Obtaining... \n");
char * new_buffer = (uint8_t *)syscall_shm_obtain(keyn, &size);
printf("XXX: Should blit old buffer onto new buffer here!\n");
printf("Redirecting compositor-side buffer to new buffer.\n");
window->buffer = new_buffer;
2012-03-02 07:13:52 +04:00
//syscall_shm_release(key);
2012-02-12 04:54:34 +04:00
}
window->x = left;
window->y = top;
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) {
2012-02-12 04:54:34 +04:00
/* Construct the header and packet */
wins_packet_t header;
header.magic = WINS_MAGIC;
2012-02-12 04:54:34 +04:00
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?
2012-03-15 00:04:12 +04:00
#if 0
2012-02-26 10:45:46 +04:00
if (command == WC_NEWWINDOW) {
fprintf(stderr, "> Creating a window. Sending a packet of size %d+%d\n", sizeof(wins_packet_t), sizeof(w_window_t));
}
2012-03-15 00:04:12 +04:00
#endif
#if 0
2012-02-12 04:54:34 +04:00
write(process_windows->command_pipe, &header, sizeof(wins_packet_t));
write(process_windows->command_pipe, &packet, sizeof(w_window_t));
#endif
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);
2012-02-12 04:54:34 +04:00
/* Now wait for the command to be processed before returning */
if (wait_for_reply) {
2012-02-13 05:30:36 +04:00
syscall_send_signal(process_windows->pid, SIGWINEVENT);
2012-03-02 07:13:52 +04:00
while((wins_command_recvd & 0xF) != (command & 0xF)) {
syscall_yield();
}
2012-02-12 04:54:34 +04:00
}
UNLOCK(wins_command_lock);
}
window_t * window_create (int16_t left, int16_t top, uint16_t width, uint16_t height) {
2012-02-12 04:54:34 +04:00
wins_send_command(0, left, top, width, height, WC_NEWWINDOW, 1);
2012-03-02 07:13:52 +04:00
while (!wins_last_new) {
syscall_yield();
}
2012-02-14 02:21:52 +04:00
2012-02-12 04:54:34 +04:00
return (window_t *)wins_last_new;
}
void window_resize (window_t * window, int16_t left, int16_t top, uint16_t width, uint16_t height) {
2012-02-12 04:54:34 +04:00
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) {
2012-02-12 04:54:34 +04:00
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);
}
2012-03-02 07:13:52 +04:00
void window_redraw_wait (window_t * window) {
wins_send_command(window->wid, 0, 0, window->width, window->height, WC_REDRAW, 1);
}
2012-02-12 04:54:34 +04:00
void window_destroy (window_t * window) {
2012-03-24 02:44:37 +04:00
wins_send_command(window->wid, 0, 0, 0, 0, WC_DESTROY, 1);
free_window_client(window);
2012-02-12 04:54:34 +04:00
}
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);
}
2012-02-12 04:54:34 +04:00
/* Event Processing (invoked by signal only) */
uint8_t volatile key_evt_buffer_lock;
list_t * key_evt_buffer;
w_keyboard_t * poll_keyboard () {
2012-02-17 11:28:12 +04:00
w_keyboard_t * evt = NULL;
2012-02-12 04:54:34 +04:00
LOCK(key_evt_buffer_lock);
if (key_evt_buffer->length > 0) {
node_t * n = list_dequeue(key_evt_buffer);
2012-02-12 04:54:34 +04:00
evt = (w_keyboard_t *)n->value;
free(n);
}
UNLOCK(key_evt_buffer_lock);
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 */
2012-02-17 11:28:12 +04:00
//LOCK(key_evt_buffer_lock);
2012-02-12 04:54:34 +04:00
list_insert(key_evt_buffer, evt);
2012-02-17 11:28:12 +04:00
//UNLOCK(key_evt_buffer_lock);
2012-02-12 04:54:34 +04:00
}
uint8_t volatile mouse_evt_buffer_lock;
list_t * mouse_evt_buffer;
w_mouse_t * poll_mouse () {
w_mouse_t * evt = NULL;
2012-02-12 04:54:34 +04:00
//LOCK(mouse_evt_buffer_lock);
2012-02-12 04:54:34 +04:00
if (mouse_evt_buffer->length > 0) {
node_t * n = list_dequeue(mouse_evt_buffer);
2012-02-12 04:54:34 +04:00
evt = (w_mouse_t *)n->value;
free(n);
}
//UNLOCK(mouse_evt_buffer_lock);
2012-02-12 04:54:34 +04:00
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 */
2012-02-17 11:28:12 +04:00
//LOCK(mouse_evt_buffer_lock);
if (mouse_evt_buffer->length > 5000) {
node_t * n = list_dequeue(mouse_evt_buffer);
free(n->value);
free(n);
}
if (mouse_action_callback) {
mouse_action_callback(evt);
}
2012-02-12 04:54:34 +04:00
list_insert(mouse_evt_buffer, evt);
2012-02-17 11:28:12 +04:00
//UNLOCK(mouse_evt_buffer_lock);
2012-02-12 04:54:34 +04:00
}
static void process_window_evt (uint8_t command, w_window_t evt) {
switch (command) {
window_t * window = NULL;
case WE_NEWWINDOW:
2012-02-13 05:30:36 +04:00
window = init_window_client(process_windows, evt.wid, evt.left, evt.top, evt.width, evt.height, 0);
2012-02-12 04:54:34 +04:00
wins_last_new = window;
break;
case WE_RESIZED:
/* XXX: We need a lock or something to contend the window buffer */
2012-10-14 00:02:58 +04:00
fprintf(stderr, "Received WINDOW_RESIZED event\n");
2012-02-12 04:54:34 +04:00
window = get_window(evt.wid);
if (!window) {
fprintf(stderr, "[%d] [window] SEVERE: wins sent WE_RESIZED for window we don't have!\n", getpid());
}
2012-10-14 00:02:58 +04:00
resize_window_buffer_client(window, evt.left, evt.top, evt.width, evt.height);
if (resize_window_callback) {
resize_window_callback(window);
}
2012-02-12 04:54:34 +04:00
break;
}
wins_command_recvd = command;
}
static void process_evt (int sig) {
/* Are there any messages in this process's event pipe? */
struct stat buf;
2012-04-13 08:30:04 +04:00
if (!process_windows) return;
2012-02-12 04:54:34 +04:00
fstat(process_windows->event_pipe, &buf);
/* Read them all out */
while (buf.st_size > 0) {
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);
}
2012-02-17 11:28:12 +04:00
2012-02-12 04:54:34 +04:00
/* Determine type, read, and dispatch */
2012-02-13 05:30:36 +04:00
switch (header.command_type & WE_GROUP_MASK) {
2012-02-12 04:54:34 +04:00
case WE_MOUSE_EVT: {
w_mouse_t * mevt = malloc(sizeof(w_mouse_t));
read(process_windows->event_pipe, mevt, sizeof(w_mouse_t));
2012-02-12 04:54:34 +04:00
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);
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);
}
2012-02-12 04:54:34 +04:00
break;
}
fstat(process_windows->event_pipe, &buf);
}
}
void install_signal_handlers () {
syscall_signal(SIGWINEVENT, process_evt); // SIGWINEVENT
2012-02-12 04:54:34 +04:00
key_evt_buffer = list_create();
mouse_evt_buffer = list_create();
}
static void ignore(int sig) {
return;
}
void * win_threaded_event_processor(void * garbage) {
while (1) {
process_evt (0);
syscall_yield();
}
}
void win_use_threaded_handler() {
syscall_signal(SIGWINEVENT, ignore); // SIGWINEVENT
pthread_t event_thread;
pthread_create(&event_thread, NULL, win_threaded_event_processor, NULL);
}
2012-02-12 04:54:34 +04:00
/* 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);
2012-02-12 04:54:34 +04:00
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. */
2012-03-15 00:04:12 +04:00
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);
2012-02-12 04:54:34 +04:00
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;
2012-02-14 02:21:52 +04:00
2012-03-02 07:13:52 +04:00
while (!wins_globals->server_done) {
syscall_yield();
}
2012-02-12 04:54:34 +04:00
2012-02-14 02:21:52 +04:00
2012-02-12 04:54:34 +04:00
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");
2012-02-12 04:54:34 +04:00
2012-02-13 04:47:01 +04:00
if (process_windows->event_pipe < 0) {
2012-02-14 02:21:52 +04:00
fprintf(stderr, "ERROR: Failed to initialize an event pipe!\n");
return 1;
2012-02-13 04:47:01 +04:00
}
2012-02-12 04:54:34 +04:00
/* 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() {
2012-03-24 02:44:37 +04:00
#if 0
2012-02-12 04:54:34 +04:00
syscall_shm_release(WINS_SERVER_IDENTIFIER);
2012-03-24 02:44:37 +04:00
#endif
2012-02-12 04:54:34 +04:00
if (wins_globals) {
//free((wins_server_global_t *)wins_globals);
2012-02-12 04:54:34 +04:00
wins_globals = NULL;
}
}
/* Client Setup/Teardown */
int setup_windowing () {
if (!process_windows) {
process_windows = malloc(sizeof(process_windows_t));
process_windows->windows = list_create();
}
install_signal_handlers();
return wins_connect();
}
void teardown_windowing () {
if (process_windows) {
window_t * window;
2012-03-24 02:44:37 +04:00
node_t * node;
while ((node = list_pop(process_windows->windows)) != NULL) {
window = node->value;
2012-02-26 08:47:20 +04:00
if (!window) break;
2012-02-12 04:54:34 +04:00
window_destroy(window);
}
free(process_windows->windows);
free(process_windows);
process_windows = NULL;
}
wins_disconnect();
}