[yutani] Alternate cursor types.
Resize and drag cursors have been added. These new cursor types are exposed in two ways: - Window drag and resize will automatically use the resize cursors if a cursor is enabled for the window. - Clients can use new values for yutani_window_show_mouse to set the current cursor type, or reset it to the previous normal or hidden state. The latter functionality is now used in the decoration library to present the appropriate resize cursor when the mouse is hovered over the decoration borders. More cursor options may be added in the future. Cursor themes will be added in the future as well. Cursors are stored in /usr/share/cursor The arrow cursor has been moved to /usr/share/cursor/normal.png ADDENDUM: A critical heisenbug with window resizing has been fixed in this commit involving a race with window dimensions and potentially also buffers.
BIN
hdd/usr/share/cursor/drag.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 718 B After Width: | Height: | Size: 718 B |
BIN
hdd/usr/share/cursor/resize-dlur.png
Normal file
After Width: | Height: | Size: 951 B |
BIN
hdd/usr/share/cursor/resize-horizontal.png
Normal file
After Width: | Height: | Size: 643 B |
BIN
hdd/usr/share/cursor/resize-uldr.png
Normal file
After Width: | Height: | Size: 854 B |
BIN
hdd/usr/share/cursor/resize-vertical.png
Normal file
After Width: | Height: | Size: 710 B |
@ -40,6 +40,9 @@
|
||||
#define YUTANI_RESIZE_RIGHT 0
|
||||
#define YUTANI_INCOMING_MOUSE_SCALE * 3
|
||||
|
||||
#define MOUSE_WIDTH 64
|
||||
#define MOUSE_HEIGHT 64
|
||||
|
||||
struct {
|
||||
int nested;
|
||||
int nest_width;
|
||||
@ -372,6 +375,7 @@ static yutani_server_window_t * server_window_create(yutani_globals_t * yg, int
|
||||
win->tiled = 0;
|
||||
win->untiled_width = 0;
|
||||
win->untiled_height = 0;
|
||||
win->default_mouse = 1;
|
||||
|
||||
char key[1024];
|
||||
YUTANI_SHMKEY(yg->server_ident, key, 1024, win);
|
||||
@ -445,22 +449,24 @@ static void server_window_resize_finish(yutani_globals_t * yg, yutani_server_win
|
||||
|
||||
mark_window(yg, win);
|
||||
|
||||
spin_lock(&yg->redraw_lock);
|
||||
|
||||
win->width = width;
|
||||
win->height = height;
|
||||
|
||||
win->bufid = win->newbufid;
|
||||
win->buffer = win->newbuffer;
|
||||
|
||||
win->newbuffer = NULL;
|
||||
win->newbufid = 0;
|
||||
|
||||
{
|
||||
char key[1024];
|
||||
YUTANI_SHMKEY_EXP(yg->server_ident, key, 1024, oldbufid);
|
||||
spin_lock(&yg->redraw_lock);
|
||||
syscall_shm_release(key);
|
||||
spin_unlock(&yg->redraw_lock);
|
||||
}
|
||||
|
||||
win->buffer = win->newbuffer;
|
||||
win->newbuffer = NULL;
|
||||
spin_unlock(&yg->redraw_lock);
|
||||
|
||||
mark_window(yg, win);
|
||||
}
|
||||
@ -623,16 +629,6 @@ static void load_fonts(yutani_globals_t * yg) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the cursor sprite.
|
||||
*
|
||||
* TODO This should probably use Cairo's PNG functionality, or something
|
||||
* else other than our own rendering tools...
|
||||
*/
|
||||
static void draw_cursor(yutani_globals_t * yg, int x, int y) {
|
||||
draw_sprite(yg->backend_ctx, &yg->mouse_sprite, x / MOUSE_SCALE - MOUSE_OFFSET_X, y / MOUSE_SCALE - MOUSE_OFFSET_Y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a clip region from a rectangle.
|
||||
*/
|
||||
@ -665,6 +661,52 @@ static void yutani_set_clip(yutani_globals_t * yg) {
|
||||
cairo_clip(yg->real_ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the cursor sprite.
|
||||
*
|
||||
* TODO This should probably use Cairo's PNG functionality, or something
|
||||
* else other than our own rendering tools...
|
||||
*/
|
||||
static void draw_cursor(yutani_globals_t * yg, int x, int y, int cursor) {
|
||||
sprite_t * sprite = &yg->mouse_sprite;
|
||||
static sprite_t * previous = NULL;
|
||||
if (yg->resizing_window) {
|
||||
switch (yg->resizing_direction) {
|
||||
case SCALE_UP:
|
||||
case SCALE_DOWN:
|
||||
sprite = &yg->mouse_sprite_resize_v;
|
||||
break;
|
||||
case SCALE_LEFT:
|
||||
case SCALE_RIGHT:
|
||||
sprite = &yg->mouse_sprite_resize_h;
|
||||
break;
|
||||
case SCALE_DOWN_RIGHT:
|
||||
case SCALE_UP_LEFT:
|
||||
sprite = &yg->mouse_sprite_resize_da;
|
||||
break;
|
||||
case SCALE_DOWN_LEFT:
|
||||
case SCALE_UP_RIGHT:
|
||||
sprite = &yg->mouse_sprite_resize_db;
|
||||
break;
|
||||
}
|
||||
} else if (yg->mouse_state == YUTANI_MOUSE_STATE_MOVING) {
|
||||
sprite = &yg->mouse_sprite_drag;
|
||||
} else {
|
||||
switch (cursor) {
|
||||
case YUTANI_CURSOR_TYPE_DRAG: sprite = &yg->mouse_sprite_drag; break;
|
||||
case YUTANI_CURSOR_TYPE_RESIZE_VERTICAL: sprite = &yg->mouse_sprite_resize_v; break;
|
||||
case YUTANI_CURSOR_TYPE_RESIZE_HORIZONTAL: sprite = &yg->mouse_sprite_resize_h; break;
|
||||
case YUTANI_CURSOR_TYPE_RESIZE_UP_DOWN: sprite = &yg->mouse_sprite_resize_da; break;
|
||||
case YUTANI_CURSOR_TYPE_RESIZE_DOWN_UP: sprite = &yg->mouse_sprite_resize_db; break;
|
||||
}
|
||||
}
|
||||
if (sprite != previous) {
|
||||
yutani_add_clip(yg, x / MOUSE_SCALE - MOUSE_OFFSET_X, y / MOUSE_SCALE - MOUSE_OFFSET_Y, MOUSE_WIDTH, MOUSE_HEIGHT);
|
||||
previous = sprite;
|
||||
}
|
||||
draw_sprite(yg->backend_ctx, sprite, x / MOUSE_SCALE - MOUSE_OFFSET_X, y / MOUSE_SCALE - MOUSE_OFFSET_Y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a window has a solid pixel at a given screen-space coordinate.
|
||||
*
|
||||
@ -1000,8 +1042,8 @@ static void redraw_windows(yutani_globals_t * yg) {
|
||||
/* If the mouse has moved, that counts as two damage regions */
|
||||
if ((yg->last_mouse_x != tmp_mouse_x) || (yg->last_mouse_y != tmp_mouse_y)) {
|
||||
has_updates = 2;
|
||||
yutani_add_clip(yg, yg->last_mouse_x / MOUSE_SCALE - MOUSE_OFFSET_X, yg->last_mouse_y / MOUSE_SCALE - MOUSE_OFFSET_Y, 64, 64);
|
||||
yutani_add_clip(yg, tmp_mouse_x / MOUSE_SCALE - MOUSE_OFFSET_X, tmp_mouse_y / MOUSE_SCALE - MOUSE_OFFSET_Y, 64, 64);
|
||||
yutani_add_clip(yg, yg->last_mouse_x / MOUSE_SCALE - MOUSE_OFFSET_X, yg->last_mouse_y / MOUSE_SCALE - MOUSE_OFFSET_Y, MOUSE_WIDTH, MOUSE_HEIGHT);
|
||||
yutani_add_clip(yg, tmp_mouse_x / MOUSE_SCALE - MOUSE_OFFSET_X, tmp_mouse_y / MOUSE_SCALE - MOUSE_OFFSET_Y, MOUSE_WIDTH, MOUSE_HEIGHT);
|
||||
}
|
||||
|
||||
yg->last_mouse_x = tmp_mouse_x;
|
||||
@ -1042,7 +1084,6 @@ static void redraw_windows(yutani_globals_t * yg) {
|
||||
*/
|
||||
spin_lock(&yg->redraw_lock);
|
||||
yutani_blit_windows(yg, yg->framebuffer_ctx);
|
||||
spin_unlock(&yg->redraw_lock);
|
||||
|
||||
if (yg->resizing_window) {
|
||||
/* Draw box */
|
||||
@ -1087,7 +1128,7 @@ static void redraw_windows(yutani_globals_t * yg) {
|
||||
*/
|
||||
yutani_server_window_t * tmp_window = top_at(yg, yg->mouse_x / MOUSE_SCALE, yg->mouse_y / MOUSE_SCALE);
|
||||
if (!tmp_window || tmp_window->show_mouse) {
|
||||
draw_cursor(yg, tmp_mouse_x, tmp_mouse_y);
|
||||
draw_cursor(yg, tmp_mouse_x, tmp_mouse_y, tmp_window ? tmp_window->show_mouse : 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1099,6 +1140,7 @@ static void redraw_windows(yutani_globals_t * yg) {
|
||||
cairo_set_source_surface(yg->real_ctx, yg->framebuffer_surface, 0, 0);
|
||||
cairo_paint(yg->real_ctx);
|
||||
}
|
||||
spin_unlock(&yg->redraw_lock);
|
||||
|
||||
/*
|
||||
* If any windows were marked for removal,
|
||||
@ -1631,6 +1673,7 @@ static void mouse_start_resize(yutani_globals_t * yg, yutani_scale_direction_t d
|
||||
|
||||
yg->resizing_direction = direction;
|
||||
make_top(yg, yg->mouse_window);
|
||||
mark_window(yg, yg->mouse_window);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1899,7 +1942,14 @@ int main(int argc, char * argv[]) {
|
||||
load_fonts(yg);
|
||||
TRACE("Done.");
|
||||
|
||||
load_sprite_png(&yg->mouse_sprite, "/usr/share/arrow.png");
|
||||
load_sprite_png(&yg->mouse_sprite, "/usr/share/cursor/normal.png");
|
||||
|
||||
load_sprite_png(&yg->mouse_sprite_drag, "/usr/share/cursor/drag.png");
|
||||
load_sprite_png(&yg->mouse_sprite_resize_v, "/usr/share/cursor/resize-vertical.png");
|
||||
load_sprite_png(&yg->mouse_sprite_resize_h, "/usr/share/cursor/resize-horizontal.png");
|
||||
load_sprite_png(&yg->mouse_sprite_resize_da, "/usr/share/cursor/resize-uldr.png");
|
||||
load_sprite_png(&yg->mouse_sprite_resize_db, "/usr/share/cursor/resize-dlur.png");
|
||||
|
||||
yg->last_mouse_x = 0;
|
||||
yg->last_mouse_y = 0;
|
||||
yg->mouse_x = yg->width * MOUSE_SCALE / 2;
|
||||
@ -2218,9 +2268,16 @@ int main(int argc, char * argv[]) {
|
||||
struct yutani_msg_window_show_mouse * wa = (void *)m->data;
|
||||
yutani_server_window_t * w = hashmap_get(yg->wids_to_windows, (void *)wa->wid);
|
||||
if (w) {
|
||||
w->show_mouse = wa->show_mouse;
|
||||
if (wa->show_mouse == -1) {
|
||||
w->show_mouse = w->default_mouse;
|
||||
} else if (wa->show_mouse < 2) {
|
||||
w->default_mouse = wa->show_mouse;
|
||||
w->show_mouse = wa->show_mouse;
|
||||
} else {
|
||||
w->show_mouse = wa->show_mouse;
|
||||
}
|
||||
if (yg->focused_window == w) {
|
||||
yutani_add_clip(yg, yg->mouse_x / MOUSE_SCALE - MOUSE_OFFSET_X, yg->mouse_y / MOUSE_SCALE - MOUSE_OFFSET_Y, 64, 64);
|
||||
yutani_add_clip(yg, yg->mouse_x / MOUSE_SCALE - MOUSE_OFFSET_X, yg->mouse_y / MOUSE_SCALE - MOUSE_OFFSET_Y, MOUSE_WIDTH, MOUSE_HEIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,8 @@ typedef struct {
|
||||
int tiled;
|
||||
int32_t untiled_width;
|
||||
int32_t untiled_height;
|
||||
|
||||
int default_mouse;
|
||||
} yutani_server_window_t;
|
||||
|
||||
typedef struct {
|
||||
@ -154,6 +156,14 @@ typedef struct {
|
||||
int32_t resizing_offset_y;
|
||||
int resizing_button;
|
||||
|
||||
sprite_t mouse_sprite_drag;
|
||||
sprite_t mouse_sprite_resize_v;
|
||||
sprite_t mouse_sprite_resize_h;
|
||||
sprite_t mouse_sprite_resize_da;
|
||||
sprite_t mouse_sprite_resize_db;
|
||||
|
||||
int current_cursor;
|
||||
|
||||
} yutani_globals_t;
|
||||
|
||||
struct key_bind {
|
||||
|
@ -250,6 +250,30 @@ static int within_decors(yutani_window_t * window, int x, int y) {
|
||||
#define TOP_SIDE (me->new_y <= decor_top_height)
|
||||
#define BOTTOM_SIDE (me->new_y >= window->height - decor_bottom_height)
|
||||
|
||||
static yutani_scale_direction_t check_resize_direction(struct yutani_msg_window_mouse_event * me, yutani_window_t * window) {
|
||||
yutani_scale_direction_t resize_direction = SCALE_NONE;
|
||||
if (LEFT_SIDE && !TOP_SIDE && !BOTTOM_SIDE) {
|
||||
resize_direction = SCALE_LEFT;
|
||||
} else if (RIGHT_SIDE && !TOP_SIDE && !BOTTOM_SIDE) {
|
||||
resize_direction = SCALE_RIGHT;
|
||||
} else if (BOTTOM_SIDE && !LEFT_SIDE && !RIGHT_SIDE) {
|
||||
resize_direction = SCALE_DOWN;
|
||||
} else if (BOTTOM_SIDE && LEFT_SIDE) {
|
||||
resize_direction = SCALE_DOWN_LEFT;
|
||||
} else if (BOTTOM_SIDE && RIGHT_SIDE) {
|
||||
resize_direction = SCALE_DOWN_RIGHT;
|
||||
} else if (TOP_SIDE && LEFT_SIDE) {
|
||||
resize_direction = SCALE_UP_LEFT;
|
||||
} else if (TOP_SIDE && RIGHT_SIDE) {
|
||||
resize_direction = SCALE_UP_RIGHT;
|
||||
} else if (TOP_SIDE && (me->new_y < 5)) {
|
||||
resize_direction = SCALE_UP;
|
||||
}
|
||||
return resize_direction;
|
||||
}
|
||||
|
||||
static int old_resize_direction = SCALE_NONE;
|
||||
|
||||
int decor_handle_event(yutani_t * yctx, yutani_msg_t * m) {
|
||||
if (m) {
|
||||
switch (m->type) {
|
||||
@ -263,24 +287,7 @@ int decor_handle_event(yutani_t * yctx, yutani_msg_t * m) {
|
||||
if (me->command == YUTANI_MOUSE_EVENT_DOWN && me->buttons & YUTANI_MOUSE_BUTTON_LEFT) {
|
||||
if (!button) {
|
||||
/* Resize edges */
|
||||
yutani_scale_direction_t resize_direction = SCALE_NONE;
|
||||
if (LEFT_SIDE && !TOP_SIDE && !BOTTOM_SIDE) {
|
||||
resize_direction = SCALE_LEFT;
|
||||
} else if (RIGHT_SIDE && !TOP_SIDE && !BOTTOM_SIDE) {
|
||||
resize_direction = SCALE_RIGHT;
|
||||
} else if (BOTTOM_SIDE && !LEFT_SIDE && !RIGHT_SIDE) {
|
||||
resize_direction = SCALE_DOWN;
|
||||
} else if (BOTTOM_SIDE && LEFT_SIDE) {
|
||||
resize_direction = SCALE_DOWN_LEFT;
|
||||
} else if (BOTTOM_SIDE && RIGHT_SIDE) {
|
||||
resize_direction = SCALE_DOWN_RIGHT;
|
||||
} else if (TOP_SIDE && LEFT_SIDE) {
|
||||
resize_direction = SCALE_UP_LEFT;
|
||||
} else if (TOP_SIDE && RIGHT_SIDE) {
|
||||
resize_direction = SCALE_UP_RIGHT;
|
||||
} else if (TOP_SIDE && (me->new_y < 5)) {
|
||||
resize_direction = SCALE_UP;
|
||||
}
|
||||
yutani_scale_direction_t resize_direction = check_resize_direction(me, window);
|
||||
|
||||
if (resize_direction != SCALE_NONE) {
|
||||
yutani_window_resize_start(yctx, window, resize_direction);
|
||||
@ -292,6 +299,37 @@ int decor_handle_event(yutani_t * yctx, yutani_msg_t * m) {
|
||||
return DECOR_OTHER;
|
||||
}
|
||||
}
|
||||
if (me->command == YUTANI_MOUSE_EVENT_MOVE) {
|
||||
if (!button) {
|
||||
/* Resize edges */
|
||||
yutani_scale_direction_t resize_direction = check_resize_direction(me, window);
|
||||
if (resize_direction != old_resize_direction) {
|
||||
if (resize_direction == SCALE_NONE) {
|
||||
yutani_window_show_mouse(yctx, window, YUTANI_CURSOR_TYPE_RESET);
|
||||
} else {
|
||||
switch (resize_direction) {
|
||||
case SCALE_UP:
|
||||
case SCALE_DOWN:
|
||||
yutani_window_show_mouse(yctx, window, YUTANI_CURSOR_TYPE_RESIZE_VERTICAL);
|
||||
break;
|
||||
case SCALE_LEFT:
|
||||
case SCALE_RIGHT:
|
||||
yutani_window_show_mouse(yctx, window, YUTANI_CURSOR_TYPE_RESIZE_HORIZONTAL);
|
||||
break;
|
||||
case SCALE_DOWN_RIGHT:
|
||||
case SCALE_UP_LEFT:
|
||||
yutani_window_show_mouse(yctx, window, YUTANI_CURSOR_TYPE_RESIZE_UP_DOWN);
|
||||
break;
|
||||
case SCALE_DOWN_LEFT:
|
||||
case SCALE_UP_RIGHT:
|
||||
yutani_window_show_mouse(yctx, window, YUTANI_CURSOR_TYPE_RESIZE_DOWN_UP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
old_resize_direction = resize_direction;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (me->command == YUTANI_MOUSE_EVENT_CLICK) {
|
||||
/* Determine if we clicked on a button */
|
||||
switch (button) {
|
||||
@ -306,6 +344,11 @@ int decor_handle_event(yutani_t * yctx, yutani_msg_t * m) {
|
||||
}
|
||||
return button;
|
||||
}
|
||||
} else {
|
||||
if (old_resize_direction != SCALE_NONE) {
|
||||
yutani_window_show_mouse(yctx, window, YUTANI_CURSOR_TYPE_RESET);
|
||||
old_resize_direction = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -335,6 +335,36 @@ typedef struct yutani_window {
|
||||
#define YUTANI_SHAPE_THRESHOLD_ANY 255
|
||||
#define YUTANI_SHAPE_THRESHOLD_PASSTHROUGH 256
|
||||
|
||||
/*
|
||||
* YUTANI_CURSOR_TYPE
|
||||
*
|
||||
* Used with SHOW_MOUSE to set the cursor type for this window.
|
||||
* Note that modifications made to the cursor will only display
|
||||
* while it the current window is active and that cursor settings
|
||||
* are per-window, not per-application.
|
||||
*
|
||||
* HIDE: Disable the mouse cursor. Useful for games.
|
||||
* NORMAL: The normal arrow cursor.
|
||||
* DRAG: A 4-directional arrow.
|
||||
* RESIZE_VERTICAL: An up-down arrow / resize indicator.
|
||||
* RESIZE_HORIZONTAL: A left-right arrow / resize indicator.
|
||||
* RESIZE_UP_DOWN: A diagonal \-shaped arrow.
|
||||
* RESIZE_DOWN_UP: A diagonal /-shaped arrow.
|
||||
*
|
||||
* RESET: If the cursor was previously hidden, hide it again.
|
||||
* Otherwise, show the normal cursor. Allows for decorator
|
||||
* to set resize cursors without having to know if a window
|
||||
* had set the default mode to HIDE.
|
||||
*/
|
||||
#define YUTANI_CURSOR_TYPE_RESET -1
|
||||
#define YUTANI_CURSOR_TYPE_HIDE 0
|
||||
#define YUTANI_CURSOR_TYPE_NORMAL 1
|
||||
#define YUTANI_CURSOR_TYPE_DRAG 2
|
||||
#define YUTANI_CURSOR_TYPE_RESIZE_VERTICAL 3
|
||||
#define YUTANI_CURSOR_TYPE_RESIZE_HORIZONTAL 4
|
||||
#define YUTANI_CURSOR_TYPE_RESIZE_UP_DOWN 5
|
||||
#define YUTANI_CURSOR_TYPE_RESIZE_DOWN_UP 6
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
|