toaruos/userspace/gui/basic/select-wallpaper.c
2016-09-02 19:30:28 +09:00

449 lines
10 KiB
C

/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2015 Kevin Lange
*
* Desktop Background Selection Tool
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <syscall.h>
#include <signal.h>
#include <errno.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "lib/yutani.h"
#include "lib/graphics.h"
#include "lib/decorations.h"
#include "gui/ttk/ttk.h"
#include "lib/hashmap.h"
#include "lib/confreader.h"
#include "lib/list.h"
#include "lib/trace.h"
#define TRACE_APP_NAME "select-wallpaper"
#define LINE_LEN 4096
#define DEFAULT_WALLPAPER "/usr/share/wallpapers/yosemite.png"
static yutani_t * yctx;
static yutani_window_t * win;
static gfx_context_t * ctx;
static cairo_surface_t * surface;
static cairo_t * cr;
static int loading = 1;
static sprite_t * wallpaper_sprite;
static list_t * wallpapers;
struct wallpaper {
char * path;
sprite_t * sprite;
};
static int wallpaper_pid = 0;
static node_t * selected_wallpaper = NULL;
static char * selected_path = NULL;
static int should_exit = 0;
static int center_x(int x) {
return (yctx->display_width - x) / 2;
}
static int center_y(int y) {
return (yctx->display_height - y) / 2;
}
static int center_win_x(int x) {
return (win->width - x) / 2;
}
#define BUTTON_HEIGHT 32
#define BUTTON_WIDTH 100
struct button {
int left, top, width, height;
int hover;
const char * label;
void (*callback)(struct button *);
};
static list_t * buttons_list = NULL;
static int draw_buttons(void) {
foreach(node, buttons_list) {
struct button * this = (struct button *)node->value;
if (this->hover == 2) {
_ttk_draw_button_select(cr, this->left, this->top, this->width, this->height, (char *)this->label);
} else if (this->hover == 1) {
_ttk_draw_button_hover(cr, this->left, this->top, this->width, this->height, (char *)this->label);
} else {
_ttk_draw_button(cr, this->left, this->top, this->width, this->height, (char *)this->label);
}
}
}
static void redraw(void) {
draw_fill(ctx, rgb(TTK_BACKGROUND_DEFAULT));
/* Draw the current tutorial frame */
render_decorations(win, ctx, "Select Desktop Background");
if (loading) {
set_font_face(FONT_SANS_SERIF);
set_font_size(12);
char * label = "Loading...";
int y = 200;
int x = center_win_x(draw_string_width(label));
draw_string(ctx, x, y, rgb(0,0,0), label);
} else {
draw_sprite(ctx, wallpaper_sprite, center_win_x(wallpaper_sprite->width), 80);
}
draw_buttons();
flip(ctx);
yutani_flip(yctx, win);
}
static int find_wallpaper_pid(void) {
DIR * dirp = opendir("/proc");
int out_pid = 0;
/* Read the entries in the directory */
list_t * ents_list = list_create();
struct dirent * ent = readdir(dirp);
while (ent != NULL) {
if (ent->d_name[0] >= '0' && ent->d_name[0] <= '9') {
char tmp[256], buf[4096], name[128];
FILE * f;
int read = 1;
char line[LINE_LEN];
snprintf(tmp, 256, "/proc/%s/status", ent->d_name);
f = fopen(tmp, "r");
while (fgets(line, LINE_LEN, f) != NULL) {
if (strstr(line, "Name:") == line) {
sscanf(line, "%s %s", &buf, &name);
if (!strcmp(name, "wallpaper")) {
out_pid = atoi(ent->d_name);
break;
}
}
}
fclose(f);
if (out_pid) break;
}
ent = readdir(dirp);
}
closedir(dirp);
return out_pid;
}
sprite_t * load_wallpaper(char * file) {
sprite_t * o_wallpaper = NULL;
sprite_t * wallpaper_tmp = calloc(1,sizeof(sprite_t));
load_sprite_png(wallpaper_tmp, file);
int width = 500;
int height = 300;
float x = (float)width / (float)wallpaper_tmp->width;
float y = (float)height / (float)wallpaper_tmp->height;
int nh = (int)(x * (float)wallpaper_tmp->height);
int nw = (int)(y * (float)wallpaper_tmp->width);;
o_wallpaper = create_sprite(width, height, ALPHA_OPAQUE);
gfx_context_t * tmp = init_graphics_sprite(o_wallpaper);
if (nw > width) {
draw_sprite_scaled(tmp, wallpaper_tmp, (width - nw) / 2, 0, nw, height);
} else {
draw_sprite_scaled(tmp, wallpaper_tmp, 0, (height - nh) / 2, width, nh);
}
free(tmp);
sprite_free(wallpaper_tmp);
return o_wallpaper;
}
sprite_t * load_current(void) {
char f_name[512];
sprintf(f_name, "%s/.desktop.conf", getenv("HOME"));
confreader_t * conf = confreader_load(f_name);
char * file = confreader_getd(conf, "", "wallpaper", DEFAULT_WALLPAPER);
selected_path = strdup(file);
sprite_t * out = load_wallpaper(file);
confreader_free(conf);
return out;
}
static struct button * focused_button = NULL;
static int previous_buttons = 0;
static void do_mouse_stuff(struct yutani_msg_window_mouse_event * me) {
if (focused_button) {
/* See if we released and are still inside. */
if (me->command == YUTANI_MOUSE_EVENT_RAISE || me->command == YUTANI_MOUSE_EVENT_CLICK) {
if (!(me->buttons & YUTANI_MOUSE_BUTTON_LEFT)) {
struct button * this = focused_button;
if (me->new_x > this->left
&& me->new_x < this->left + this->width
&& me->new_y > this->top
&& me->new_y < this->top + this->height) {
this->hover = 1;
this->callback(this);
focused_button = NULL;
redraw();
} else {
this->hover = 0;
focused_button = NULL;
redraw();
}
}
}
} else {
foreach(node, buttons_list) {
struct button * this = (struct button *)node->value;
if (me->new_x > this->left
&& me->new_x < this->left + this->width
&& me->new_y > this->top
&& me->new_y < this->top + this->height) {
if (!this->hover) {
this->hover = 1;
redraw();
}
if (me->command == YUTANI_MOUSE_EVENT_DOWN && (me->buttons & YUTANI_MOUSE_BUTTON_LEFT)) {
this->hover = 2;
focused_button = this;
redraw();
}
} else {
if (this->hover) {
this->hover = 0;
redraw();
}
}
}
}
previous_buttons = me->buttons;
}
static void add_button(int x, int y, int width, int height, const char * label, void (*callback)(struct button *)) {
struct button * this = malloc(sizeof(struct button));
this->left = x;
this->top = y;
this->width = width;
this->height = height;
this->label = label;
this->callback = callback;
list_insert(buttons_list, this);
}
static void button_ok(struct button * this) {
char f_name[512];
sprintf(f_name, "%s/.desktop.conf", getenv("HOME"));
TRACE("Okay button pressed");
FILE * f = fopen(f_name, "w");
fprintf(f, "wallpaper=%s\n", selected_path);
fclose(f);
if (wallpaper_pid) {
kill(wallpaper_pid, SIGUSR1);
}
}
static void button_cancel(struct button * this) {
should_exit = 1;
}
static void button_prev(struct button * this) {
TRACE("prev");
if (!selected_wallpaper) {
selected_wallpaper = wallpapers->head;
if (!selected_wallpaper) return;
} else {
selected_wallpaper = selected_wallpaper->prev;
if (!selected_wallpaper) {
selected_wallpaper = wallpapers->head;
}
}
wallpaper_sprite = ((struct wallpaper *)selected_wallpaper->value)->sprite;
selected_path = ((struct wallpaper *)selected_wallpaper->value)->path;
redraw();
}
static void button_next(struct button * this) {
TRACE("next");
if (!selected_wallpaper) {
selected_wallpaper = wallpapers->head;
if (!selected_wallpaper) return;
} else {
selected_wallpaper = selected_wallpaper->next;
if (!selected_wallpaper) {
selected_wallpaper = wallpapers->head;
}
}
wallpaper_sprite = ((struct wallpaper *)selected_wallpaper->value)->sprite;
selected_path = ((struct wallpaper *)selected_wallpaper->value)->path;
redraw();
}
static void discover_wallpapers(void) {
wallpapers = list_create();
DIR * dirp = opendir("/usr/share/wallpapers");
struct dirent * ent = readdir(dirp);
while (ent != NULL) {
if (ent->d_name[0] != '.') {
char tmp[256];
snprintf(tmp, 256, "/usr/share/wallpapers/%s", ent->d_name);
struct wallpaper * this = malloc(sizeof(struct wallpaper));
this->path = strdup(tmp);
this->sprite = load_wallpaper(this->path);
list_insert(wallpapers, this);
}
ent = readdir(dirp);
}
closedir(dirp);
TRACE("Found %d wallpaper%s.", wallpapers->length, wallpapers->length == 1 ? "" : "s");
}
int main(int argc, char * argv[]) {
TRACE("Launching wallpaper selection...");
wallpaper_pid = find_wallpaper_pid();
TRACE("Wallpaper PID is %d", wallpaper_pid);
yctx = yutani_init();
init_decorations();
buttons_list = list_create();
add_button(410, 430, BUTTON_WIDTH, BUTTON_HEIGHT, "Apply", &button_ok);
add_button(520, 430, BUTTON_WIDTH, BUTTON_HEIGHT, "Exit", &button_cancel);
add_button(20, 200, 32, 100, "<", &button_prev);
add_button(640-20-32, 200, 32, 100, ">", &button_next);
win = yutani_window_create(yctx, 640, 480);
yutani_window_move(yctx, win, center_x(640), center_y(480));
ctx = init_graphics_yutani_double_buffer(win);
int stride;
stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, win->width);
surface = cairo_image_surface_create_for_data(ctx->backbuffer, CAIRO_FORMAT_ARGB32, win->width, win->height, stride);
cr = cairo_create(surface);
yutani_window_advertise_icon(yctx, win, "Desktop Background", "select-wallpaper");
redraw();
wallpaper_sprite = load_current();
discover_wallpapers();
loading = 0;
redraw();
yutani_focus_window(yctx, win->wid);
while (!should_exit) {
yutani_msg_t * m = yutani_poll(yctx);
if (m) {
switch (m->type) {
case YUTANI_MSG_KEY_EVENT:
{
struct yutani_msg_key_event * ke = (void*)m->data;
if (ke->event.key == 'q' && ke->event.action == KEY_ACTION_DOWN) {
should_exit = 1;
}
}
break;
case YUTANI_MSG_WINDOW_FOCUS_CHANGE:
{
struct yutani_msg_window_focus_change * wf = (void*)m->data;
if (wf->wid == win->wid) {
win->focused = wf->focused;
redraw();
}
}
break;
case YUTANI_MSG_WINDOW_MOUSE_EVENT:
{
struct yutani_msg_window_mouse_event * me = (void*)m->data;
if (me->wid != win->wid) break;
int result = decor_handle_event(yctx, m);
switch (result) {
case DECOR_CLOSE:
should_exit = 1;
break;
default:
/* Other actions */
do_mouse_stuff(me);
break;
}
}
break;
case YUTANI_MSG_SESSION_END:
should_exit = 1;
break;
default:
break;
}
free(m);
}
}
return 0;
}