2012-03-15 00:04:12 +04:00
|
|
|
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
2012-07-07 08:08:28 +04:00
|
|
|
*
|
|
|
|
* Panel
|
|
|
|
*
|
|
|
|
* Provides a graphical panel with a clock, and
|
|
|
|
* hopefully more things in the future.
|
2012-03-15 00:04:12 +04:00
|
|
|
*/
|
2012-03-08 09:44:02 +04:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <time.h>
|
2013-05-03 10:33:57 +04:00
|
|
|
#include <unistd.h>
|
2012-10-10 08:02:43 +04:00
|
|
|
#include <sys/time.h>
|
2013-03-14 08:55:25 +04:00
|
|
|
#include <sys/utsname.h>
|
2012-03-08 09:44:02 +04:00
|
|
|
|
2012-11-18 00:57:16 +04:00
|
|
|
#define PANEL_HEIGHT 28
|
|
|
|
|
2014-04-16 06:45:56 +04:00
|
|
|
#include "lib/pthread.h"
|
|
|
|
#include "lib/yutani.h"
|
2012-03-08 09:44:02 +04:00
|
|
|
#include "lib/graphics.h"
|
2012-11-19 08:14:57 +04:00
|
|
|
#include "lib/shmemfonts.h"
|
2012-03-08 09:44:02 +04:00
|
|
|
|
|
|
|
sprite_t * sprites[128];
|
|
|
|
sprite_t alpha_tmp;
|
|
|
|
|
Context-based graphics library.
All graphics library commands now take a gfx_context_t pointer, which
points to a simple datastructure describing a rendering context (width,
height, depth, total size, front buffer, backbuffer; where backbuffer =
front buffer when not in double-buffering mode, thus we always render to
backbuffer except on a flip). This may have caused a minor speed
reduction, but I don't really care as it's far more important that we
support multiple graphics contexts.
TODO:
- Shared Memory Fonts library (there are a couple of apps that use these
so-called "shmem fonts" on their own; we need a dedicated library for
them)
- Break off "TTK" GUI toolkit into its own library. Since it's just a
callback-based button framework, this shouldn't be too hard right now.
Also, with the previous tick, I'll be able to put labels on controls
and start using text in more places.
2012-04-17 22:21:34 +04:00
|
|
|
gfx_context_t * ctx;
|
2014-04-16 06:45:56 +04:00
|
|
|
yutani_t * yctx;
|
|
|
|
yutani_window_t * panel;
|
2014-04-18 11:18:19 +04:00
|
|
|
list_t * window_list;
|
2014-04-19 06:23:45 +04:00
|
|
|
volatile int lock = 0;
|
|
|
|
volatile int drawlock = 0;
|
|
|
|
|
|
|
|
size_t bg_size;
|
|
|
|
char * bg_blob;
|
2014-04-16 06:45:56 +04:00
|
|
|
|
|
|
|
int width;
|
|
|
|
int height;
|
2012-03-08 09:44:02 +04:00
|
|
|
|
|
|
|
int center_x(int x) {
|
2014-04-16 06:45:56 +04:00
|
|
|
return (width - x) / 2;
|
2012-03-08 09:44:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int center_y(int y) {
|
2014-04-16 06:45:56 +04:00
|
|
|
return (height - y) / 2;
|
2012-03-08 09:44:02 +04:00
|
|
|
}
|
|
|
|
|
2014-04-18 11:18:19 +04:00
|
|
|
static void spin_lock(int volatile * lock) {
|
|
|
|
while(__sync_lock_test_and_set(lock, 0x01)) {
|
|
|
|
syscall_yield();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void spin_unlock(int volatile * lock) {
|
|
|
|
__sync_lock_release(lock);
|
|
|
|
}
|
|
|
|
|
2012-03-08 09:44:02 +04:00
|
|
|
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 {
|
2012-09-14 09:12:47 +04:00
|
|
|
sprites[i]->alpha = ALPHA_OPAQUE;
|
2012-03-08 09:44:02 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-20 08:16:21 +04:00
|
|
|
void init_sprite_png(int i, char * filename) {
|
|
|
|
sprites[i] = malloc(sizeof(sprite_t));
|
|
|
|
load_sprite_png(sprites[i], filename);
|
|
|
|
}
|
|
|
|
|
2014-04-20 12:24:10 +04:00
|
|
|
void redraw(void);
|
|
|
|
|
2012-03-08 09:44:02 +04:00
|
|
|
#define FONT_SIZE 14
|
2013-05-03 10:33:57 +04:00
|
|
|
#define TIME_LEFT 108
|
|
|
|
#define DATE_WIDTH 70
|
2012-03-08 09:44:02 +04:00
|
|
|
|
2012-09-13 11:29:29 +04:00
|
|
|
volatile int _continue = 1;
|
|
|
|
|
2014-04-20 12:24:10 +04:00
|
|
|
/* honestly no way we're gonna fit more at the moment... */
|
|
|
|
int icon_lefts[20] = {0};
|
|
|
|
int icon_wids[20] = {0};
|
|
|
|
int focused_app = -1;
|
|
|
|
|
2014-05-03 23:22:16 +04:00
|
|
|
struct window_ad {
|
|
|
|
uint32_t flags;
|
|
|
|
char * name;
|
|
|
|
char * icon;
|
|
|
|
char * strings;
|
|
|
|
};
|
|
|
|
|
2012-09-13 11:29:29 +04:00
|
|
|
void sig_int(int sig) {
|
|
|
|
printf("Received shutdown signal in panel!\n");
|
|
|
|
_continue = 0;
|
|
|
|
}
|
|
|
|
|
2014-04-20 12:24:10 +04:00
|
|
|
void set_focused(int i) {
|
|
|
|
if (focused_app != i) {
|
|
|
|
focused_app = i;
|
|
|
|
redraw();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-19 06:23:45 +04:00
|
|
|
void panel_check_click(struct yutani_msg_window_mouse_event * evt) {
|
|
|
|
if (evt->command == YUTANI_MOUSE_EVENT_CLICK) {
|
2012-09-20 08:16:21 +04:00
|
|
|
printf("Click!\n");
|
2014-04-16 06:45:56 +04:00
|
|
|
if (evt->new_x >= width - 24 ) {
|
2014-04-19 06:23:45 +04:00
|
|
|
yutani_session_end(yctx);
|
2012-09-20 08:16:21 +04:00
|
|
|
_continue = 0;
|
2014-04-20 12:24:10 +04:00
|
|
|
} else {
|
|
|
|
for (int i = 0; i < 18; ++i) {
|
|
|
|
if (evt->new_x >= icon_lefts[i] && evt->new_x < icon_lefts[i+1]) {
|
|
|
|
if (icon_wids[i]) {
|
|
|
|
yutani_focus_window(yctx, icon_wids[i]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-04-21 00:44:17 +04:00
|
|
|
} else if (evt->command == YUTANI_MOUSE_EVENT_MOVE || evt->command == YUTANI_MOUSE_EVENT_ENTER) {
|
2014-04-20 12:24:10 +04:00
|
|
|
if (evt->new_y < PANEL_HEIGHT) {
|
|
|
|
for (int i = 0; i < 18; ++i) {
|
|
|
|
if (icon_lefts[i] == 0) {
|
|
|
|
set_focused(-1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (evt->new_x >= icon_lefts[i] && evt->new_x < icon_lefts[i+1]) {
|
|
|
|
set_focused(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
set_focused(-1);
|
2012-09-20 08:16:21 +04:00
|
|
|
}
|
2014-04-21 00:44:17 +04:00
|
|
|
} else if (evt->command == YUTANI_MOUSE_EVENT_LEAVE) {
|
|
|
|
set_focused(-1);
|
2012-09-20 08:16:21 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-19 06:23:45 +04:00
|
|
|
void redraw(void) {
|
|
|
|
spin_lock(&drawlock);
|
|
|
|
|
|
|
|
struct timeval now;
|
|
|
|
int last = 0;
|
|
|
|
struct tm * timeinfo;
|
|
|
|
char buffer[80];
|
|
|
|
|
|
|
|
uint32_t txt_color = rgb(230,230,230);
|
|
|
|
int t = 0;
|
|
|
|
|
|
|
|
memcpy(ctx->backbuffer, bg_blob, bg_size);
|
|
|
|
gettimeofday(&now, NULL);
|
|
|
|
last = now.tv_sec;
|
|
|
|
timeinfo = localtime((time_t *)&now.tv_sec);
|
|
|
|
|
|
|
|
strftime(buffer, 80, "%H:%M:%S", timeinfo);
|
|
|
|
set_font_face(FONT_SANS_SERIF_BOLD);
|
|
|
|
set_font_size(16);
|
|
|
|
draw_string(ctx, width - TIME_LEFT, 19, txt_color, buffer);
|
|
|
|
|
|
|
|
strftime(buffer, 80, "%A", timeinfo);
|
|
|
|
set_font_face(FONT_SANS_SERIF);
|
|
|
|
set_font_size(9);
|
|
|
|
t = draw_string_width(buffer);
|
|
|
|
t = (DATE_WIDTH - t) / 2;
|
|
|
|
draw_string(ctx, width - TIME_LEFT - DATE_WIDTH + t, 11, txt_color, buffer);
|
|
|
|
|
|
|
|
strftime(buffer, 80, "%h %e", timeinfo);
|
|
|
|
set_font_face(FONT_SANS_SERIF_BOLD);
|
|
|
|
set_font_size(9);
|
|
|
|
t = draw_string_width(buffer);
|
|
|
|
t = (DATE_WIDTH - t) / 2;
|
|
|
|
draw_string(ctx, width - TIME_LEFT - DATE_WIDTH + t, 21, txt_color, buffer);
|
|
|
|
|
|
|
|
set_font_face(FONT_SANS_SERIF_BOLD);
|
|
|
|
set_font_size(14);
|
|
|
|
draw_string(ctx, 10, 18, txt_color, "Applications");
|
|
|
|
|
2014-04-20 12:24:10 +04:00
|
|
|
int i = 0, j = 0;
|
2014-04-19 06:23:45 +04:00
|
|
|
spin_lock(&lock);
|
|
|
|
if (window_list) {
|
|
|
|
foreach(node, window_list) {
|
2014-05-03 23:22:16 +04:00
|
|
|
struct window_ad * ad = node->value;
|
|
|
|
char * s = ad->name;
|
2014-04-19 06:23:45 +04:00
|
|
|
|
|
|
|
set_font_face(FONT_SANS_SERIF);
|
|
|
|
set_font_size(14);
|
2014-04-20 12:24:10 +04:00
|
|
|
if (j == focused_app) {
|
|
|
|
draw_string(ctx, 140 + i, 18, rgb(142,216,255), s);
|
|
|
|
} else {
|
2014-05-03 23:22:16 +04:00
|
|
|
if (ad->flags & 1) {
|
|
|
|
draw_string(ctx, 140 + i, 18, rgb(255,0,0), s);
|
|
|
|
} else {
|
|
|
|
draw_string(ctx, 140 + i, 18, txt_color, s);
|
|
|
|
}
|
2014-04-20 12:24:10 +04:00
|
|
|
}
|
|
|
|
if (j < 18) {
|
|
|
|
icon_lefts[j] = 140 + i;
|
|
|
|
j++;
|
|
|
|
}
|
2014-04-19 06:23:45 +04:00
|
|
|
i += draw_string_width(s) + 20;
|
|
|
|
}
|
2014-04-20 12:24:10 +04:00
|
|
|
if (j < 19) {
|
|
|
|
icon_lefts[j] = 140 + i;
|
|
|
|
icon_lefts[j+1] = 0;
|
|
|
|
}
|
2014-04-19 06:23:45 +04:00
|
|
|
}
|
|
|
|
spin_unlock(&lock);
|
|
|
|
|
|
|
|
draw_sprite(ctx, sprites[1], width - 23, 1); /* Logout button */
|
|
|
|
|
|
|
|
flip(ctx);
|
|
|
|
yutani_flip(yctx, panel);
|
|
|
|
|
|
|
|
spin_unlock(&drawlock);
|
|
|
|
}
|
2014-04-20 12:24:10 +04:00
|
|
|
|
2014-04-18 11:18:19 +04:00
|
|
|
void update_window_list(void) {
|
|
|
|
yutani_query_windows(yctx);
|
|
|
|
|
|
|
|
list_t * new_window_list = list_create();
|
|
|
|
|
2014-04-20 12:24:10 +04:00
|
|
|
int i = 0;
|
|
|
|
|
2014-04-18 11:18:19 +04:00
|
|
|
while (1) {
|
|
|
|
yutani_msg_t * m = yutani_wait_for(yctx, YUTANI_MSG_WINDOW_ADVERTISE);
|
|
|
|
struct yutani_msg_window_advertise * wa = (void*)m->data;
|
|
|
|
|
|
|
|
if (wa->size == 0) {
|
|
|
|
free(m);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-04-20 12:24:10 +04:00
|
|
|
if (i < 19) {
|
|
|
|
icon_wids[i] = wa->wid;
|
|
|
|
icon_wids[i+1] = 0;
|
|
|
|
}
|
|
|
|
|
2014-05-03 23:22:16 +04:00
|
|
|
struct window_ad * ad = malloc(sizeof(struct window_ad));
|
|
|
|
|
2014-05-03 23:07:03 +04:00
|
|
|
char * s = malloc(wa->size);
|
|
|
|
memcpy(s, wa->strings, wa->size);
|
2014-05-03 23:22:16 +04:00
|
|
|
ad->name = &s[wa->offsets[0]];
|
|
|
|
ad->icon = &s[wa->offsets[1]];
|
|
|
|
ad->strings = s;
|
|
|
|
ad->flags = wa->flags;
|
2014-05-03 23:07:03 +04:00
|
|
|
|
2014-05-03 23:22:16 +04:00
|
|
|
list_insert(new_window_list, ad);
|
2014-04-18 11:18:19 +04:00
|
|
|
free(m);
|
2014-04-20 12:24:10 +04:00
|
|
|
|
|
|
|
i++;
|
2014-04-18 11:18:19 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock(&lock);
|
|
|
|
if (window_list) {
|
2014-05-03 23:22:16 +04:00
|
|
|
foreach(node, window_list) {
|
|
|
|
struct window_ad * ad = (void*)node->value;
|
|
|
|
free(ad->strings);
|
|
|
|
free(ad);
|
|
|
|
}
|
2014-04-18 11:18:19 +04:00
|
|
|
list_free(window_list);
|
|
|
|
free(window_list);
|
|
|
|
}
|
|
|
|
window_list = new_window_list;
|
|
|
|
spin_unlock(&lock);
|
2014-04-19 06:23:45 +04:00
|
|
|
|
|
|
|
redraw();
|
2014-04-18 11:18:19 +04:00
|
|
|
}
|
|
|
|
|
2014-04-16 06:45:56 +04:00
|
|
|
void * clock_thread(void * garbage) {
|
2012-09-13 11:29:29 +04:00
|
|
|
while (_continue) {
|
2014-04-19 06:23:45 +04:00
|
|
|
redraw();
|
2013-05-03 10:33:57 +04:00
|
|
|
usleep(500000);
|
2012-03-08 09:44:02 +04:00
|
|
|
}
|
2014-04-16 06:45:56 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int main (int argc, char ** argv) {
|
|
|
|
yctx = yutani_init();
|
|
|
|
|
|
|
|
width = yctx->display_width;
|
|
|
|
height = yctx->display_height;
|
|
|
|
|
|
|
|
init_shmemfonts();
|
|
|
|
set_font_size(14);
|
|
|
|
|
|
|
|
/* Create the panel */
|
|
|
|
panel = yutani_window_create(yctx, width, PANEL_HEIGHT);
|
|
|
|
yutani_set_stack(yctx, panel, YUTANI_ZORDER_TOP);
|
|
|
|
ctx = init_graphics_yutani_double_buffer(panel);
|
|
|
|
draw_fill(ctx, rgba(0,0,0,0));
|
|
|
|
flip(ctx);
|
|
|
|
yutani_flip(yctx, panel);
|
|
|
|
|
2014-04-18 11:18:19 +04:00
|
|
|
window_list = NULL;
|
|
|
|
|
|
|
|
yutani_subscribe_windows(yctx);
|
|
|
|
|
2014-04-16 06:45:56 +04:00
|
|
|
init_sprite_png(0, "/usr/share/panel.png");
|
|
|
|
init_sprite_png(1, "/usr/share/icons/panel-shutdown.png");
|
|
|
|
|
|
|
|
syscall_signal(2, sig_int);
|
|
|
|
|
2014-04-19 06:23:45 +04:00
|
|
|
for (uint32_t i = 0; i < width; i += sprites[0]->width) {
|
|
|
|
draw_sprite(ctx, sprites[0], i, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bg_size = panel->width * panel->height * sizeof(uint32_t);
|
|
|
|
bg_blob = malloc(bg_size);
|
|
|
|
memcpy(bg_blob, ctx->backbuffer, bg_size);
|
|
|
|
|
2014-04-16 06:45:56 +04:00
|
|
|
pthread_t _clock_thread;
|
|
|
|
pthread_create(&_clock_thread, NULL, clock_thread, NULL);
|
|
|
|
|
2014-04-18 11:18:19 +04:00
|
|
|
update_window_list();
|
|
|
|
|
2014-04-16 06:45:56 +04:00
|
|
|
while (_continue) {
|
|
|
|
yutani_msg_t * m = yutani_poll(yctx);
|
|
|
|
if (m) {
|
2014-04-19 06:23:45 +04:00
|
|
|
switch (m->type) {
|
|
|
|
case YUTANI_MSG_NOTIFY:
|
|
|
|
update_window_list();
|
|
|
|
break;
|
|
|
|
case YUTANI_MSG_WINDOW_MOUSE_EVENT:
|
|
|
|
panel_check_click((struct yutani_msg_window_mouse_event *)m->data);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2014-04-18 11:18:19 +04:00
|
|
|
}
|
2014-04-16 06:45:56 +04:00
|
|
|
free(m);
|
|
|
|
}
|
|
|
|
}
|
2012-03-08 09:44:02 +04:00
|
|
|
|
2014-04-16 06:45:56 +04:00
|
|
|
yutani_close(yctx, panel);
|
2014-04-18 11:18:19 +04:00
|
|
|
yutani_unsubscribe_windows(yctx);
|
2012-03-08 09:44:02 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|