toaruos/userspace/compositor.c
Kevin Lange 4c67b5da84 Completely rewrote compositing engine
The compositor itself still needs work, but the compositing engine
within now does full blitting and is faster than the old method.
Transparency is now supported properly, though telling the compositor to
use it on a window will degrade performance. One terminal is usually
okay, and everything runs faster than it did before; two terminals is
pushing it; three will make you very sad. The stacking logic has also
been updated. Presumably, alpha blitting for transparent windows could
be done with SIMD instructions and be extremely fast.

All graphics libraries have also been updated to (hopefully) work
properly with alpha bits.
2012-09-13 22:12:47 -07:00

1253 lines
34 KiB
C

/*
* Compositor
*
* This is the window compositor application.
* It serves shared memory regions to clients
* and renders them to the screen.
*/
#include <stdio.h>
#include <stdint.h>
#include <syscall.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <assert.h>
#include <sys/stat.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_CACHE_H
#include "lib/list.h"
#include "lib/graphics.h"
#include "lib/window.h"
#include "lib/pthread.h"
#include "../kernel/include/signal.h"
#include "../kernel/include/mouse.h"
void spin_lock(int volatile * lock) {
while(__sync_lock_test_and_set(lock, 0x01)) {
syscall_yield();
}
}
void spin_unlock(int volatile * lock) {
__sync_lock_release(lock);
}
window_t * windows[0x10000];
sprite_t * sprites[128];
gfx_context_t * ctx;
#define WIN_D 32
#define WIN_B (WIN_D / 8)
#define MOUSE_DISCARD_LEVEL 6
list_t * process_list;
int32_t mouse_x, mouse_y;
int32_t click_x, click_y;
uint32_t mouse_discard = 0;
#define MOUSE_SCALE 3
#define MOUSE_OFFSET_X 26
#define MOUSE_OFFSET_Y 26
void redraw_region_slow(int32_t x, int32_t y, int32_t width, int32_t height);
void redraw_cursor() {
//redraw_region_slow(mouse_x / MOUSE_SCALE - 32, mouse_y / MOUSE_SCALE - 32, 64, 64);
draw_sprite(ctx, sprites[3], mouse_x / MOUSE_SCALE - MOUSE_OFFSET_X, mouse_y / MOUSE_SCALE - MOUSE_OFFSET_Y);
}
extern 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);
extern void free_window (window_t * window);
extern void resize_window_buffer (window_t * window, int16_t left, int16_t top, uint16_t width, uint16_t height);
uint16_t * depth_map = NULL;
uintptr_t * top_map = NULL;
process_windows_t * get_process_windows (uint32_t pid) {
foreach(n, process_list) {
process_windows_t * pw = (process_windows_t *)n->value;
if (pw->pid == pid) {
return pw;
}
}
return NULL;
}
static window_t * get_window (wid_t wid) {
foreach (n, process_list) {
process_windows_t * pw = (process_windows_t *)n->value;
foreach (m, pw->windows) {
window_t * w = (window_t *)m->value;
if (w->wid == wid) {
return w;
}
}
}
return NULL;
}
static window_t * get_window_with_process (process_windows_t * pw, wid_t wid) {
foreach (m, pw->windows) {
window_t * w = (window_t *)m->value;
if (w->wid == wid) {
return w;
}
}
return NULL;
}
void init_process_list () {
process_list = list_create();
memset(windows, 0x00000000, sizeof(window_t *) * 0x10000);
}
int32_t min(int32_t a, int32_t b) {
return (a < b) ? a : b;
}
int32_t max(int32_t a, int32_t b) {
return (a > b) ? a : b;
}
uint8_t is_between(int32_t lo, int32_t hi, int32_t val) {
if (val >= lo && val < hi) return 1;
return 0;
}
uint8_t is_top(window_t *window, uint16_t x, uint16_t y) {
uint16_t index = window->z;
foreach(n, process_list) {
process_windows_t * pw = (process_windows_t *)n->value;
foreach(node, pw->windows) {
window_t * win = (window_t *)node->value;
if (win == window) continue;
if (win->z < index) continue;
if (is_between(win->x, win->x + win->width, x) && is_between(win->y, win->y + win->height, y)) {
return 0;
}
}
}
return 1;
}
uint8_t inline is_top_fast(window_t * window, uint16_t x, uint16_t y) {
if (x >= ctx->width || y >= ctx->height) {
return 0;
}
if (window->z == depth_map[x + y * ctx->width]) {
return 1;
}
return 0;
}
window_t * top_at(uint16_t x, uint16_t y) {
uint32_t index_top = 0;
window_t * window_top = NULL;
foreach(n, process_list) {
process_windows_t * pw = (process_windows_t *)n->value;
foreach(node, pw->windows) {
window_t * win = (window_t *)node->value;
if (is_between(win->x, win->x + win->width, x) && is_between(win->y, win->y + win->height, y)) {
if (window_top == NULL) {
window_top = win;
index_top = win->z;
} else {
if (win->z < index_top) continue;
window_top = win;
index_top = win->z;
}
}
}
}
return window_top;
}
void rebalance_windows() {
uint32_t i = 1;
for (; i < 0xFFF8; ++i) {
if (!windows[i]) break;
}
uint32_t j = i + 1;
for (; j < 0xFFF8; ++j) {
if (!windows[j]) break;
}
if (j == i + 1) {
printf("Nothing to reorder.\n");
return;
} else {
printf("Need to reshuffle. One moment.\n");
for (j = i; j < 0xFFF8; ++j) {
windows[j] = windows[j+1];
if (windows[j+1] == NULL) return;
windows[j]->z = j;
}
}
}
void reorder_window (window_t * window, uint16_t new_zed) {
if (!window) {
return;
}
int z = window->z;
window->z = new_zed;
if (windows[z] == window) {
windows[z] = NULL;
}
if (new_zed == 0 || new_zed == 0xFFFF) {
windows[new_zed] = window;
if (z != new_zed) {
rebalance_windows();
}
return;
}
if (windows[new_zed] != window) {
reorder_window(windows[new_zed], new_zed + 1);
windows[new_zed ] = window;
}
if (z != new_zed) {
rebalance_windows();
}
printf("Window 0x%x is now at z=%d\n", window, new_zed);
}
void make_top(window_t * window) {
uint16_t index = window->z;
if (index == 0) return;
if (index == 0xFFFF) return;
uint16_t highest = 0;
foreach(n, process_list) {
process_windows_t * pw = (process_windows_t *)n->value;
foreach(node, pw->windows) {
window_t * win = (window_t *)node->value;
if (win == window) continue;
if (win->z == 0) continue;
if (win->z == 0xFFFF) continue;
if (highest < win->z) highest = win->z;
if (win == window) continue;
if (win->z > window->z) continue;
}
}
printf("Making top will make this window stack at %d.\n", highest+1);
reorder_window(window, highest+1);
}
window_t * focused_window() {
return top_at(mouse_x / MOUSE_SCALE, mouse_y / MOUSE_SCALE);
}
volatile int am_drawing = 0;
window_t * moving_window = NULL;
int32_t moving_window_l = 0;
int32_t moving_window_t = 0;
/* Internal drawing functions */
void redraw_window(window_t *window, uint16_t x, uint16_t y, uint16_t width, uint16_t height) {
if (!window) {
return;
}
uint16_t _lo_x = max(window->x + x, 0);
uint16_t _hi_x = min(window->x + width, ctx->width);
uint16_t _lo_y = max(window->y + y, 0);
uint16_t _hi_y = min(window->y + height, ctx->height);
for (uint16_t y = _lo_y; y < _hi_y; ++y) {
for (uint16_t x = _lo_x; x < _hi_x; ++x) {
/* XXX MAKE THIS FASTER */
if (is_top_fast(window, x, y)) {
if (TO_WINDOW_OFFSET(x,y) >= window->width * window->height) continue;
GFX(ctx,x,y) = ((uint32_t *)window->buffer)[TO_WINDOW_OFFSET(x,y)];
}
}
}
//redraw_region_slow(mouse_x / MOUSE_SCALE - 32, mouse_y / MOUSE_SCALE - 32, 64, 64);
//redraw_cursor();
}
void window_add (window_t * window) {
int z = window->z;
while (windows[z]) {
z++;
}
printf("Assigning depth of %d to window 0x%x\n", z, window);
window->z = z;
windows[z] = window;
}
void unorder_window (window_t * window) {
int z = window->z;
if (z < 0x10000 && windows[z]) {
windows[z] = 0;
}
window->z = 0;
return;
}
void redraw_full_window (window_t * window) {
if (!window) {
return;
}
redraw_window(window, (uint16_t)0, (uint16_t)0, window->width, window->height);
}
void redraw_region_slow(int32_t x, int32_t y, int32_t width, int32_t height) {
uint16_t _lo_x = max(x, 0);
uint16_t _hi_x = min(x + width, ctx->width);
uint16_t _lo_y = max(y, 0);
uint16_t _hi_y = min(y + height, ctx->height);
for (uint32_t y = _lo_y; y < _hi_y; ++y) {
for (uint32_t x = _lo_x; x < _hi_x; ++x) {
window_t * window = top_at(x,y);
if (window) {
//GFX(ctx,x,y) = ((uint32_t *)window->buffer)[TO_WINDOW_OFFSET(x,y)];
depth_map[x + y * ctx->width] = window->z;
top_map[x + y * ctx->width] = (uintptr_t)window;
} else {
//GFX(ctx,x,y) = (y % 2 ^ x % 2) ? rgb(0,0,0) : rgb(255,255,255);
depth_map[x + y * ctx->width] = 0;
top_map[x + y * ctx->width] = 0;
}
}
}
}
void blit_window(window_t * window, int32_t left, int32_t top) {
#define TO_DERPED_OFFSET(x,y) (((x) - left) + ((y) - top) * window->width)
uint16_t _lo_x = max(left, 0);
uint16_t _hi_x = min(left + window->width, ctx->width);
uint16_t _lo_y = max(top, 0);
uint16_t _hi_y = min(top + window->height, ctx->height);
if (window->use_alpha) {
for (uint16_t y = _lo_y; y < _hi_y; ++y) {
for (uint16_t x = _lo_x; x < _hi_x; ++x) {
GFX(ctx,x,y) = alpha_blend_rgba(GFX(ctx,x,y), ((uint32_t *)window->buffer)[TO_DERPED_OFFSET(x,y)]);
}
}
} else {
uint16_t win_x = _lo_x - left;
uint16_t width = (_hi_x - _lo_x) * 4;
uint16_t win_y = _lo_y - top;
for (uint16_t y = _lo_y; y < _hi_y; ++y) {
win_y = y - top;
memcpy(&ctx->backbuffer[4 * (y * ctx->width + _lo_x)], &window->buffer[(win_y * window->width + win_x) * 4], width);
}
}
}
void redraw_everything_fast() {
for (uint32_t i = 0; i < 0x10000; ++i) {
window_t * window = NULL;
if (windows[i]) {
window = windows[i];
if (window == moving_window) {
blit_window(moving_window, moving_window_l, moving_window_t);
} else {
blit_window(window, window->x, window->y);
}
}
}
}
void redraw_bounding_box(window_t *window, int32_t left, int32_t top, uint32_t derped) {
return;
if (!window) {
return;
}
int32_t _min_x = max(left, 0);
int32_t _min_y = max(top, 0);
int32_t _max_x = min(left + window->width - 1, ctx->width - 1);
int32_t _max_y = min(top + window->height - 1, ctx->height - 1);
if (!derped) {
redraw_region_slow(_min_x, _min_y, (_max_x - _min_x + 1), 1);
redraw_region_slow(_min_x, _max_y, (_max_x - _min_x + 1), 1);
redraw_region_slow(_min_x, _min_y, 1, (_max_y - _min_y + 1));
redraw_region_slow(_max_x, _min_y, 1, (_max_y - _min_y + 1));
} else {
uint32_t color = rgb(255,0,0);
draw_line(ctx, _min_x, _max_x, _min_y, _min_y, color);
draw_line(ctx, _min_x, _max_x, _max_y, _max_y, color);
draw_line(ctx, _min_x, _min_x, _min_y, _max_y, color);
draw_line(ctx, _max_x, _max_x, _min_y, _max_y, color);
}
}
void redraw_bounding_box_r(window_t *window, int32_t width, int32_t height, uint32_t derped) {
return;
if (!window) {
return;
}
int32_t _min_x = max(window->x, 0);
int32_t _min_y = max(window->y, 0);
int32_t _max_x = min(window->x + width - 1, ctx->width - 1);
int32_t _max_y = min(window->y + height - 1, ctx->height - 1);
if (!derped) {
redraw_region_slow(_min_x, _min_y, (_max_x - _min_x + 1), 1);
redraw_region_slow(_min_x, _max_y, (_max_x - _min_x + 1), 1);
redraw_region_slow(_min_x, _min_y, 1, (_max_y - _min_y + 1));
redraw_region_slow(_max_x, _min_y, 1, (_max_y - _min_y + 1));
} else {
uint32_t color = rgb(0,255,0);
draw_line(ctx, _min_x, _max_x, _min_y, _min_y, color);
draw_line(ctx, _min_x, _max_x, _max_y, _max_y, color);
draw_line(ctx, _min_x, _min_x, _min_y, _max_y, color);
draw_line(ctx, _max_x, _max_x, _min_y, _max_y, color);
}
}
wid_t volatile _next_wid = 0;
void send_window_event (process_windows_t * pw, uint8_t event, w_window_t * packet) {
/* Construct the header */
wins_packet_t header;
header.magic = WINS_MAGIC;
header.command_type = event;
header.packet_size = sizeof(w_window_t);
/* Send them */
// XXX: we have a race condition here
write(pw->event_pipe, &header, sizeof(wins_packet_t));
write(pw->event_pipe, packet, sizeof(w_window_t));
syscall_send_signal(pw->pid, SIGWINEVENT); // SIGWINEVENT
syscall_yield();
}
void send_keyboard_event (process_windows_t * pw, uint8_t event, w_keyboard_t packet) {
/* Construct the header */
wins_packet_t header;
header.magic = WINS_MAGIC;
header.command_type = event;
header.packet_size = sizeof(w_keyboard_t);
/* Send them */
// XXX: we have a race condition here
write(pw->event_pipe, &header, sizeof(wins_packet_t));
write(pw->event_pipe, &packet, sizeof(w_keyboard_t));
syscall_send_signal(pw->pid, SIGWINEVENT); // SIGWINEVENT
syscall_yield();
}
FILE *fdopen(int fd, const char *mode);
void send_mouse_event (process_windows_t * pw, uint8_t event, w_mouse_t * packet) {
/* Construct the header */
wins_packet_t header;
header.magic = WINS_MAGIC;
header.command_type = event;
header.packet_size = sizeof(w_mouse_t);
/* Send them */
fwrite(&header, 1, sizeof(wins_packet_t), pw->event_pipe_file);
fwrite(packet, 1, sizeof(w_mouse_t), pw->event_pipe_file);
fflush(pw->event_pipe_file);
//syscall_send_signal(pw->pid, SIGWINEVENT); // SIGWINEVENT
//syscall_yield();
}
void process_window_command (int sig) {
foreach(n, process_list) {
process_windows_t * pw = (process_windows_t *)n->value;
/* Are there any messages in this process's command pipe? */
struct stat buf;
fstat(pw->command_pipe, &buf);
int max_requests_per_cycle = 1;
while ((buf.st_size > 0) && (max_requests_per_cycle > 0)) {
#if 0
if (!(buf.st_size > sizeof(wins_packet_t))) {
fstat(pw->command_pipe, &buf);
continue;
}
#endif
w_window_t wwt;
wins_packet_t header;
int bytes_read = read(pw->command_pipe, &header, sizeof(wins_packet_t));
while (header.magic != WINS_MAGIC) {
printf("Magic is wrong from pid %d, expected 0x%x but got 0x%x [read %d bytes of %d]\n", pw->pid, WINS_MAGIC, header.magic, bytes_read, sizeof(header));
max_requests_per_cycle--;
goto bad_magic;
memcpy(&header, (void *)((uintptr_t)&header + 1), (sizeof(header) - 1));
read(pw->event_pipe, (char *)((uintptr_t)&header + sizeof(header) - 1), 1);
}
max_requests_per_cycle--;
switch (header.command_type) {
case WC_NEWWINDOW:
{
printf("[compositor] New window request\n");
read(pw->command_pipe, &wwt, sizeof(w_window_t));
wwt.wid = _next_wid;
window_t * new_window = init_window(pw, _next_wid, wwt.left, wwt.top, wwt.width, wwt.height, _next_wid); //XXX: an actual index
window_add(new_window);
_next_wid++;
send_window_event(pw, WE_NEWWINDOW, &wwt);
redraw_region_slow(0,0,ctx->width,ctx->height);
}
break;
case WC_SET_ALPHA:
{
read(pw->command_pipe, &wwt, sizeof(w_window_t));
window_t * window = get_window_with_process(pw, wwt.wid);
window->use_alpha = 1;
}
break;
case WC_RESIZE:
read(pw->command_pipe, &wwt, sizeof(w_window_t));
resize_window_buffer(get_window(wwt.wid), wwt.left, wwt.top, wwt.width, wwt.height);
send_window_event(pw, WE_RESIZED, &wwt);
break;
case WC_DESTROY:
read(pw->command_pipe, &wwt, sizeof(w_window_t));
window_t * win = get_window_with_process(pw, wwt.wid);
win->x = 0xFFFF;
unorder_window(win);
redraw_region_slow(0,0,ctx->width,ctx->height);
/* Wait until we're done drawing */
spin_lock(&am_drawing);
spin_unlock(&am_drawing);
free_window(win);
send_window_event(pw, WE_DESTROYED, &wwt);
break;
case WC_DAMAGE:
read(pw->command_pipe, &wwt, sizeof(w_window_t));
//redraw_window(get_window_with_process(pw, wwt.wid), wwt.left, wwt.top, wwt.width, wwt.height);
break;
case WC_REDRAW:
read(pw->command_pipe, &wwt, sizeof(w_window_t));
//redraw_window(get_window_with_process(pw, wwt.wid), wwt.left, wwt.top, wwt.width, wwt.height);
send_window_event(pw, WE_REDRAWN, &wwt);
break;
case WC_REORDER:
read(pw->command_pipe, &wwt, sizeof(w_window_t));
reorder_window(get_window_with_process(pw, wwt.wid), wwt.left);
redraw_region_slow(0,0,ctx->width,ctx->height);
break;
default:
printf("[compositor] WARN: Unknown command type %d...\n", header.command_type);
void * nullbuf = malloc(header.packet_size);
read(pw->command_pipe, nullbuf, header.packet_size);
free(nullbuf);
break;
}
bad_magic:
fstat(pw->command_pipe, &buf);
}
}
syscall_yield();
}
void waitabit() {
int x = time(NULL);
while (time(NULL) < x + 1) {
syscall_yield();
}
}
/* Request page system */
wins_server_global_t volatile * _request_page;
void reset_request_system () {
_request_page->lock = 0;
_request_page->server_done = 0;
_request_page->client_done = 0;
_request_page->client_pid = 0;
_request_page->event_pipe = 0;
_request_page->command_pipe = 0;
_request_page->server_pid = getpid();
_request_page->server_width = ctx->width;
_request_page->server_height = ctx->height;
_request_page->server_depth = ctx->depth;
_request_page->magic = WINS_MAGIC;
}
void init_request_system () {
size_t size = sizeof(wins_server_global_t);
_request_page = (wins_server_global_t *)syscall_shm_obtain(WINS_SERVER_IDENTIFIER, &size);
if (!_request_page) {
fprintf(stderr, "[wins] Could not get a shm block for its request page! Bailing...");
exit(-1);
}
reset_request_system();
}
void process_request () {
fflush(stdout);
if (_request_page->client_done) {
process_windows_t * pw = malloc(sizeof(process_windows_t));
pw->pid = _request_page->client_pid;
pw->event_pipe = syscall_mkpipe();
pw->event_pipe_file = fdopen(pw->event_pipe, "a");
pw->command_pipe = syscall_mkpipe();
pw->windows = list_create();
_request_page->event_pipe = syscall_share_fd(pw->event_pipe, pw->pid);
_request_page->command_pipe = syscall_share_fd(pw->command_pipe, pw->pid);
_request_page->client_done = 0;
_request_page->server_done = 1;
list_insert(process_list, pw);
}
if (!_request_page->lock) {
reset_request_system();
}
}
void delete_process (process_windows_t * pw) {
list_destroy(pw->windows);
list_free(pw->windows);
free(pw->windows);
close(pw->command_pipe);
close(pw->event_pipe);
node_t * n = list_find(process_list, pw);
list_delete(process_list, n);
free(n);
free(pw);
}
/* Signals */
void * ignore(void * value) {
return NULL;
}
void init_signal_handlers () {
#if 0
syscall_signal(SIGWINEVENT, process_window_command); // SIGWINEVENT
#else
syscall_signal(SIGWINEVENT, ignore); // SIGWINEVENT
#endif
}
/* Sprite stuff */
sprite_t alpha_tmp;
void init_sprite(int i, char * filename, char * alpha) {
sprites[i] = malloc(sizeof(sprite_t));
load_sprite(sprites[i], filename);
if (alpha) {
sprites[i]->alpha = 1;
load_sprite(&alpha_tmp, alpha);
sprites[i]->masks = alpha_tmp.bitmap;
} else {
sprites[i]->alpha = 0;
}
sprites[i]->blank = 0x0;
}
int center_x(int x) {
return (ctx->width - x) / 2;
}
int center_y(int y) {
return (ctx->height - y) / 2;
}
static int progress = 0;
static int progress_width = 0;
#define PROGRESS_WIDTH 120
#define PROGRESS_HEIGHT 6
#define PROGRESS_OFFSET 50
void draw_progress() {
int x = center_x(PROGRESS_WIDTH);
int y = center_y(0);
uint32_t color = rgb(0,120,230);
uint32_t fill = rgb(0,70,160);
draw_line(ctx, x, x + PROGRESS_WIDTH, y + PROGRESS_OFFSET, y + PROGRESS_OFFSET, color);
draw_line(ctx, x, x + PROGRESS_WIDTH, y + PROGRESS_OFFSET + PROGRESS_HEIGHT, y + PROGRESS_OFFSET + PROGRESS_HEIGHT, color);
draw_line(ctx, x, x, y + PROGRESS_OFFSET, y + PROGRESS_OFFSET + PROGRESS_HEIGHT, color);
draw_line(ctx, x + PROGRESS_WIDTH, x + PROGRESS_WIDTH, y + PROGRESS_OFFSET, y + PROGRESS_OFFSET + PROGRESS_HEIGHT, color);
if (progress_width > 0) {
int width = ((PROGRESS_WIDTH - 2) * progress) / progress_width;
for (int8_t i = 0; i < PROGRESS_HEIGHT - 1; ++i) {
draw_line(ctx, x + 1, x + 1 + width, y + PROGRESS_OFFSET + i + 1, y + PROGRESS_OFFSET + i + 1, fill);
}
}
}
uint32_t gradient_at(uint16_t j) {
float x = j * 80;
x = x / ctx->height;
return rgb(0, 1 * x, 2 * x);
}
void display() {
for (uint16_t j = 0; j < ctx->height; ++j) {
draw_line(ctx, 0, ctx->width, j, j, gradient_at(j));
}
draw_sprite(ctx, sprites[0], center_x(sprites[0]->width), center_y(sprites[0]->height));
draw_progress();
flip(ctx);
}
typedef struct {
void (*func)();
char * name;
int time;
} startup_item;
list_t * startup_items;
void add_startup_item(char * name, void (*func)(), int time) {
progress_width += time;
startup_item * item = malloc(sizeof(startup_item));
item->name = name;
item->func = func;
item->time = time;
list_insert(startup_items, item);
}
static void test() {
/* Do Nothing */
}
void run_startup_item(startup_item * item) {
item->func();
progress += item->time;
}
FT_Library library;
FT_Face face;
FT_Face face_bold;
FT_Face face_italic;
FT_Face face_bold_italic;
FT_Face face_extra;
FT_GlyphSlot slot;
FT_UInt glyph_index;
char * loadMemFont(char * ident, char * name, size_t * size) {
FILE * f = fopen(name, "r");
size_t s = 0;
fseek(f, 0, SEEK_END);
s = ftell(f);
fseek(f, 0, SEEK_SET);
size_t shm_size = s;
char * font = (char *)syscall_shm_obtain(ident, &shm_size); //malloc(s);
assert((shm_size >= s) && "shm_obtain returned too little memory to load a font into!");
fread(font, s, 1, f);
fclose(f);
*size = s;
return font;
}
void _init_freetype() {
int error;
error = FT_Init_FreeType(&library);
}
#define FONT_SIZE 13
#define ACTUALLY_LOAD_FONTS 0
int error;
void _load_dejavu() {
char * font;
size_t s;
font = loadMemFont(WINS_SERVER_IDENTIFIER ".fonts.sans-serif", "/usr/share/fonts/DejaVuSans.ttf", &s);
#if ACTUALLY_LOAD_FONTS
error = FT_New_Memory_Face(library, font, s, 0, &face);
error = FT_Set_Pixel_Sizes(face, FONT_SIZE, FONT_SIZE);
#endif
}
void _load_dejavubold() {
char * font;
size_t s;
font = loadMemFont(WINS_SERVER_IDENTIFIER ".fonts.sans-serif.bold", "/usr/share/fonts/DejaVuSans-Bold.ttf", &s);
#if ACTUALLY_LOAD_FONTS
error = FT_New_Memory_Face(library, font, s, 0, &face_bold);
error = FT_Set_Pixel_Sizes(face_bold, FONT_SIZE, FONT_SIZE);
#endif
}
void _load_dejavuitalic() {
char * font;
size_t s;
font = loadMemFont(WINS_SERVER_IDENTIFIER ".fonts.sans-serif.italic", "/usr/share/fonts/DejaVuSans-Oblique.ttf", &s);
#if ACTUALLY_LOAD_FONTS
error = FT_New_Memory_Face(library, font, s, 0, &face_italic);
error = FT_Set_Pixel_Sizes(face_italic, FONT_SIZE, FONT_SIZE);
#endif
}
void _load_dejavubolditalic() {
char * font;
size_t s;
font = loadMemFont(WINS_SERVER_IDENTIFIER ".fonts.sans-serif.bolditalic", "/usr/share/fonts/DejaVuSans-BoldOblique.ttf", &s);
#if ACTUALLY_LOAD_FONTS
error = FT_New_Memory_Face(library, font, s, 0, &face_bold_italic);
error = FT_Set_Pixel_Sizes(face_bold_italic, FONT_SIZE, FONT_SIZE);
#endif
}
void _load_dejamonovu() {
char * font;
size_t s;
font = loadMemFont(WINS_SERVER_IDENTIFIER ".fonts.monospace", "/usr/share/fonts/DejaVuSansMono.ttf", &s);
#if ACTUALLY_LOAD_FONTS
error = FT_New_Memory_Face(library, font, s, 0, &face);
error = FT_Set_Pixel_Sizes(face, FONT_SIZE, FONT_SIZE);
#endif
}
void _load_dejamonovubold() {
char * font;
size_t s;
font = loadMemFont(WINS_SERVER_IDENTIFIER ".fonts.monospace.bold", "/usr/share/fonts/DejaVuSansMono-Bold.ttf", &s);
#if ACTUALLY_LOAD_FONTS
error = FT_New_Memory_Face(library, font, s, 0, &face_bold);
error = FT_Set_Pixel_Sizes(face_bold, FONT_SIZE, FONT_SIZE);
#endif
}
void _load_dejamonovuitalic() {
char * font;
size_t s;
font = loadMemFont(WINS_SERVER_IDENTIFIER ".fonts.monospace.italic", "/usr/share/fonts/DejaVuSansMono-Oblique.ttf", &s);
#if ACTUALLY_LOAD_FONTS
error = FT_New_Memory_Face(library, font, s, 0, &face_italic);
error = FT_Set_Pixel_Sizes(face_italic, FONT_SIZE, FONT_SIZE);
#endif
}
void _load_dejamonovubolditalic() {
char * font;
size_t s;
font = loadMemFont(WINS_SERVER_IDENTIFIER ".fonts.monospace.bolditalic", "/usr/share/fonts/DejaVuSansMono-BoldOblique.ttf", &s);
#if ACTUALLY_LOAD_FONTS
error = FT_New_Memory_Face(library, font, s, 0, &face_bold_italic);
error = FT_Set_Pixel_Sizes(face_bold_italic, FONT_SIZE, FONT_SIZE);
#endif
}
void init_base_windows () {
process_windows_t * pw = malloc(sizeof(process_windows_t));
pw->pid = getpid();
pw->command_pipe = syscall_mkpipe(); /* nothing in here */
pw->event_pipe = syscall_mkpipe(); /* nothing in here */
pw->windows = list_create();
list_insert(process_list, pw);
#if 1
/* Create the background window */
#if 0
window_t * root = init_window(pw, _next_wid++, 0, 0, ctx->width, ctx->height, 0);
window_draw_sprite(root, sprites[1], 0, 0);
redraw_full_window(root);
#endif
#if 0
/* Create the panel */
window_t * panel = init_window(pw, _next_wid++, 0, 0, ctx->width, 24, 0xFFFF);
window_fill(panel, rgb(0,120,230));
init_sprite(2, "/usr/share/panel.bmp", NULL);
for (uint32_t i = 0; i < ctx->width; i += sprites[2]->width) {
window_draw_sprite(panel, sprites[2], i, 0);
}
redraw_full_window(panel);
redraw_region_slow(0,0,ctx->width,ctx->height);
#endif
#endif
init_sprite(3, "/usr/share/arrow.bmp","/usr/share/arrow_alpha.bmp");
}
void * process_requests(void * garbage) {
int mfd = *((int *)garbage);
mouse_x = MOUSE_SCALE * ctx->width / 2;
mouse_y = MOUSE_SCALE * ctx->height / 2;
click_x = 0;
click_y = 0;
uint16_t _mouse_state = 0;
window_t * _mouse_window = NULL;
int32_t _mouse_init_x;
int32_t _mouse_init_y;
int32_t _mouse_win_x;
int32_t _mouse_win_y;
int8_t _mouse_moved = 0;
int32_t _mouse_win_x_p;
int32_t _mouse_win_y_p;
struct stat _stat;
char buf[1024];
while (1) {
fstat(mfd, &_stat);
while (_stat.st_size >= sizeof(mouse_device_packet_t)) {
mouse_device_packet_t * packet = (mouse_device_packet_t *)&buf;
int r = read(mfd, &buf, sizeof(mouse_device_packet_t));
#if 1
if (packet->magic != MOUSE_MAGIC) {
int r = read(mfd, buf, 1);
break;
}
//redraw_region_slow(mouse_x / MOUSE_SCALE - 32, mouse_y / MOUSE_SCALE - 32, 64, 64);
/* Apply mouse movement */
int c, l;
c = abs(packet->x_difference);
l = 0;
while (c >>= 1) {
l++;
}
mouse_x += packet->x_difference * l;
c = abs(packet->y_difference);
l = 0;
while (c >>= 1) {
l++;
}
mouse_y -= packet->y_difference * l;
if (mouse_x < 0) mouse_x = 0;
if (mouse_y < 0) mouse_y = 0;
if (mouse_x >= ctx->width * MOUSE_SCALE) mouse_x = (ctx->width) * MOUSE_SCALE;
if (mouse_y >= ctx->height * MOUSE_SCALE) mouse_y = (ctx->height) * MOUSE_SCALE;
//draw_sprite(sprites[3], mouse_x / MOUSE_SCALE - MOUSE_OFFSET_X, mouse_y / MOUSE_SCALE - MOUSE_OFFSET_Y);
if (_mouse_state == 0 && (packet->buttons & MOUSE_BUTTON_RIGHT)) {
_mouse_window = focused_window();
if (_mouse_window) {
if (_mouse_window->z == 0 || _mouse_window->z == 0xFFFF) {
redraw_region_slow(0,0,ctx->width,ctx->height);
/* *sigh* */
} else {
_mouse_state = 1;
_mouse_init_x = mouse_x;
_mouse_init_y = mouse_y;
_mouse_win_x = _mouse_window->x;
_mouse_win_y = _mouse_window->y;
_mouse_win_x_p = _mouse_win_x;
_mouse_win_y_p = _mouse_win_y;
moving_window = _mouse_window;
moving_window_l = _mouse_win_x_p;
moving_window_t = _mouse_win_y_p;
make_top(_mouse_window);
redraw_region_slow(0,0,ctx->width,ctx->height);
}
}
} else if (_mouse_state == 0 && (packet->buttons & MOUSE_BUTTON_LEFT)) {
_mouse_window = focused_window();
if (_mouse_window) {
_mouse_state = 2; /* Dragging */
/* In window coordinates, that's... */
_mouse_win_x = _mouse_window->x;
_mouse_win_y = _mouse_window->y;
click_x = mouse_x / MOUSE_SCALE - _mouse_win_x;
click_y = mouse_y / MOUSE_SCALE - _mouse_win_y;
mouse_discard = 1;
_mouse_moved = 0;
printf("Mouse down at @ %d,%d = %d,%d\n", mouse_x, mouse_y, click_x, click_y);
}
#if 0
_mouse_window = focused_window();
if (_mouse_window) {
if (_mouse_window->z == 0 || _mouse_window->z == 0xFFFF) {
} else {
_mouse_state = 2;
_mouse_init_x = mouse_x;
_mouse_init_y = mouse_y;
_mouse_win_x = _mouse_window->width;
_mouse_win_y = _mouse_window->height;
_mouse_win_x_p= _mouse_win_x;
_mouse_win_y_p= _mouse_win_y;
make_top(_mouse_window);
redraw_region_slow(0,0,ctx->width,ctx->height);
}
}
#endif
} else if (_mouse_state == 1) {
if (!(packet->buttons & MOUSE_BUTTON_RIGHT)) {
_mouse_window->x = _mouse_win_x + (mouse_x - _mouse_init_x) / MOUSE_SCALE;
_mouse_window->y = _mouse_win_y + (mouse_y - _mouse_init_y) / MOUSE_SCALE;
moving_window = NULL;
redraw_region_slow(0,0,ctx->width,ctx->height);
_mouse_state = 0;
} else {
redraw_bounding_box(_mouse_window, _mouse_win_x_p, _mouse_win_y_p, 0);
_mouse_win_x_p = _mouse_win_x + (mouse_x - _mouse_init_x) / MOUSE_SCALE;
_mouse_win_y_p = _mouse_win_y + (mouse_y - _mouse_init_y) / MOUSE_SCALE;
moving_window = _mouse_window;
moving_window_l = _mouse_win_x_p;
moving_window_t = _mouse_win_y_p;
redraw_bounding_box(_mouse_window, _mouse_win_x_p, _mouse_win_y_p, 1);
}
} else if (_mouse_state == 2) {
if (!(packet->buttons & MOUSE_BUTTON_LEFT)) {
/* Released */
_mouse_state = 0;
_mouse_win_x = _mouse_window->x;
_mouse_win_y = _mouse_window->y;
click_x = mouse_x / MOUSE_SCALE - _mouse_win_x;
click_y = mouse_y / MOUSE_SCALE - _mouse_win_y;
if (!_mouse_moved) {
printf("Finished a click!\n");
w_mouse_t _packet;
_packet.wid = _mouse_window->wid;
_mouse_win_x = _mouse_window->x;
_mouse_win_y = _mouse_window->y;
click_x = mouse_x / MOUSE_SCALE - _mouse_win_x;
click_y = mouse_y / MOUSE_SCALE - _mouse_win_y;
_packet.new_x = click_x;
_packet.new_y = click_y;
_packet.old_x = -1;
_packet.old_y = -1;
_packet.buttons = packet->buttons;
_packet.command = WE_MOUSECLICK;
send_mouse_event(_mouse_window->owner, WE_MOUSEMOVE, &_packet);
}
printf("Mouse up at @ %d,%d = %d,%d\n", mouse_x, mouse_y, click_x, click_y);
#if 0 /* Resizing */
_mouse_win_x_p = _mouse_win_x + (mouse_x - _mouse_init_x) / MOUSE_SCALE;
_mouse_win_y_p = _mouse_win_y + (mouse_y - _mouse_init_y) / MOUSE_SCALE;
if (_mouse_win_x_p < 10) _mouse_win_x_p = 10;
if (_mouse_win_y_p < 10) _mouse_win_y_p = 10;
printf("resizing window to %d x %d\n", _mouse_win_x_p, _mouse_win_y_p);
resize_window_buffer(_mouse_window, _mouse_window->x, _mouse_window->y, _mouse_win_x_p, _mouse_win_y_p);
w_window_t tmp;
tmp.left = _mouse_window->x;
tmp.top = _mouse_window->y;
tmp.wid = _mouse_window->wid;
tmp.width = _mouse_win_x_p;
tmp.height = _mouse_win_y_p;
send_window_event(_mouse_window->owner, WE_RESIZED, &tmp);
redraw_region_slow(0,0,ctx->width,ctx->height);
_mouse_state = 0;
#endif
} else {
/* Still down */
_mouse_moved = 1;
mouse_discard--;
if (mouse_discard < 1) {
mouse_discard = MOUSE_DISCARD_LEVEL;
w_mouse_t _packet;
_packet.wid = _mouse_window->wid;
_mouse_win_x = _mouse_window->x;
_mouse_win_y = _mouse_window->y;
_packet.old_x = click_x;
_packet.old_y = click_y;
click_x = mouse_x / MOUSE_SCALE - _mouse_win_x;
click_y = mouse_y / MOUSE_SCALE - _mouse_win_y;
_packet.new_x = click_x;
_packet.new_y = click_y;
_packet.buttons = packet->buttons;
_packet.command = WE_MOUSEMOVE;
send_mouse_event(_mouse_window->owner, WE_MOUSEMOVE, &_packet);
}
#if 0
redraw_bounding_box_r(_mouse_window, _mouse_win_x_p, _mouse_win_y_p, 0);
_mouse_win_x_p = _mouse_win_x + (mouse_x - _mouse_init_x) / MOUSE_SCALE;
_mouse_win_y_p = _mouse_win_y + (mouse_y - _mouse_init_y) / MOUSE_SCALE;
if (_mouse_win_x_p < 10) _mouse_win_x_p = 10;
if (_mouse_win_y_p < 10) _mouse_win_y_p = 10;
redraw_bounding_box_r(_mouse_window, _mouse_win_x_p, _mouse_win_y_p, 1);
#endif
}
}
#if 1
if (packet->buttons & MOUSE_BUTTON_MIDDLE) {
printf("middle click @%dx%d!\n", mouse_x / MOUSE_SCALE, mouse_y / MOUSE_SCALE);
window_t * focused = focused_window();
if (focused) {
if (focused->z != 0 && focused->z != 0xFFFF) {
free_window(focused);
}
redraw_region_slow(0,0,ctx->width,ctx->height);
}
}
#endif
#endif
fstat(mfd, &_stat);
}
fstat(0, &_stat);
if (_stat.st_size) {
int r = read(0, buf, 1);
if (r > 0) {
w_keyboard_t packet;
window_t * focused = focused_window();
if (focused) {
packet.wid = focused->wid;
packet.command = 0;
packet.key = (uint16_t)buf[0];
send_keyboard_event(focused->owner, WE_KEYDOWN, packet);
}
}
}
}
return NULL;
}
void * redraw_thread(void * derp) {
while (1) {
spin_lock(&am_drawing);
redraw_everything_fast();
/* Other stuff */
redraw_cursor();
spin_unlock(&am_drawing);
flip(ctx);
syscall_yield();
}
}
int main(int argc, char ** argv) {
/* Initialize graphics setup */
ctx = init_graphics_fullscreen_double_buffer();
depth_map = malloc(sizeof(uint16_t) * ctx->width * ctx->height);
top_map = malloc(sizeof(uintptr_t) * ctx->width * ctx->height);
/* Initialize the client request system */
init_request_system();
/* Initialize process list */
init_process_list();
/* Initialize signal handlers */
init_signal_handlers();
/* Load sprites */
init_sprite(0, "/usr/share/bs.bmp", "/usr/share/bs-alpha.bmp");
display();
/* Count startup items */
startup_items = list_create();
add_startup_item("Initializing FreeType", _init_freetype, 1);
#if 1
add_startup_item("Loading font: Deja Vu Sans", _load_dejavu, 2);
add_startup_item("Loading font: Deja Vu Sans Bold", _load_dejavubold, 2);
add_startup_item("Loading font: Deja Vu Sans Oblique", _load_dejavuitalic, 2);
add_startup_item("Loading font: Deja Vu Sans Bold+Oblique", _load_dejavubolditalic, 2);
add_startup_item("Loading font: Deja Vu Sans Mono", _load_dejamonovu, 2);
add_startup_item("Loading font: Deja Vu Sans Mono Bold", _load_dejamonovubold, 2);
add_startup_item("Loading font: Deja Vu Sans Mono Oblique", _load_dejamonovuitalic, 2);
add_startup_item("Loading font: Deja Vu Sans Mono Bold+Oblique", _load_dejamonovubolditalic, 2);
#endif
foreach(node, startup_items) {
run_startup_item((startup_item *)node->value);
display();
}
#if 0
/* Reinitialize for single buffering */
init_graphics();
#endif
/* Create the root and panel */
init_base_windows();
process_windows_t * rootpw = get_process_windows(getpid());
if (!rootpw) {
printf("[compositor] SEVERE: No root process windows!\n");
return 1;
}
/* Grab the mouse */
int mfd = syscall_mousedevice();
pthread_t input_thread;
pthread_create(&input_thread, NULL, process_requests, (void *)&mfd);
pthread_t redraw_everything_thread;
pthread_create(&redraw_everything_thread, NULL, redraw_thread, NULL);
if (!fork()) {
char * args[] = {"/bin/glogin", NULL};
execve(args[0], args, NULL);
}
/* Sit in a run loop */
while (1) {
process_request();
process_window_command(0);
syscall_yield();
}
// XXX: Better have SIGINT/SIGSTOP handlers
return 0;
}