1919 lines
45 KiB
C
1919 lines
45 KiB
C
#include <hikari/view.h>
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#include <wlr/types/wlr_cursor.h>
|
|
|
|
#include <hikari/color.h>
|
|
#include <hikari/configuration.h>
|
|
#include <hikari/geometry.h>
|
|
#include <hikari/group.h>
|
|
#include <hikari/indicator.h>
|
|
#include <hikari/layout.h>
|
|
#include <hikari/mark.h>
|
|
#include <hikari/memory.h>
|
|
#include <hikari/operation.h>
|
|
#include <hikari/output.h>
|
|
#include <hikari/server.h>
|
|
#include <hikari/sheet.h>
|
|
#include <hikari/tile.h>
|
|
#include <hikari/view_config.h>
|
|
#include <hikari/workspace.h>
|
|
#include <hikari/xdg_view.h>
|
|
#include <hikari/xwayland_view.h>
|
|
|
|
#define VIEW(name, link) \
|
|
static struct hikari_view *name##_view(void) \
|
|
{ \
|
|
struct wl_list *views = hikari_server.visible_views.link; \
|
|
\
|
|
if (!wl_list_empty(views)) { \
|
|
struct hikari_view *view; \
|
|
view = wl_container_of(views, view, visible_server_views); \
|
|
return view; \
|
|
} \
|
|
\
|
|
return NULL; \
|
|
} \
|
|
\
|
|
static bool is_##name##_view(struct hikari_view *view) \
|
|
{ \
|
|
return view == name##_view(); \
|
|
}
|
|
|
|
VIEW(first, next)
|
|
VIEW(last, prev)
|
|
#undef VIEW
|
|
|
|
static void
|
|
move_to_top(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
assert(hikari_view_is_mapped(view));
|
|
|
|
wl_list_remove(&view->sheet_views);
|
|
wl_list_insert(&view->sheet->views, &view->sheet_views);
|
|
|
|
wl_list_remove(&view->group_views);
|
|
wl_list_insert(&view->group->views, &view->group_views);
|
|
|
|
wl_list_remove(&view->output_views);
|
|
wl_list_insert(&view->output->views, &view->output_views);
|
|
}
|
|
|
|
static void
|
|
place_visibly_above(
|
|
struct hikari_view *view, struct hikari_workspace *workspace)
|
|
{
|
|
assert(hikari_view_is_forced(view) ? hikari_view_is_hidden(view)
|
|
: !hikari_view_is_hidden(view));
|
|
|
|
wl_list_remove(&view->visible_server_views);
|
|
wl_list_insert(&hikari_server.visible_views, &view->visible_server_views);
|
|
|
|
wl_list_remove(&view->visible_group_views);
|
|
wl_list_insert(&view->group->visible_views, &view->visible_group_views);
|
|
|
|
wl_list_remove(&view->group->visible_server_groups);
|
|
wl_list_insert(
|
|
&hikari_server.visible_groups, &view->group->visible_server_groups);
|
|
|
|
wl_list_remove(&view->workspace_views);
|
|
wl_list_insert(&workspace->views, &view->workspace_views);
|
|
}
|
|
|
|
static void
|
|
raise_view(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
|
|
move_to_top(view);
|
|
place_visibly_above(view, view->sheet->workspace);
|
|
}
|
|
|
|
static void
|
|
refresh_border_geometry(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
hikari_border_refresh_geometry(&view->border, view->current_geometry);
|
|
hikari_indicator_frame_refresh_geometry(&view->indicator_frame, view);
|
|
}
|
|
|
|
static inline void
|
|
move_view_constrained(
|
|
struct hikari_view *view, struct wlr_box *geometry, int x, int y)
|
|
{
|
|
if (!hikari_view_is_hidden(view)) {
|
|
hikari_view_damage_whole(view);
|
|
hikari_indicator_damage(&hikari_server.indicator, view);
|
|
}
|
|
|
|
hikari_geometry_constrain_relative(
|
|
geometry, &view->output->usable_area, x, y);
|
|
}
|
|
|
|
static void
|
|
move_view(struct hikari_view *view, struct wlr_box *geometry, int x, int y)
|
|
{
|
|
if (view->maximized_state != NULL) {
|
|
struct wlr_box *usable_area;
|
|
|
|
switch (view->maximized_state->maximization) {
|
|
case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED:
|
|
return;
|
|
|
|
case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED:
|
|
usable_area = &view->output->usable_area;
|
|
|
|
if (y != usable_area->y) {
|
|
return;
|
|
}
|
|
|
|
move_view_constrained(view, geometry, x, y);
|
|
view->geometry.x = x;
|
|
break;
|
|
|
|
case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED:
|
|
usable_area = &view->output->usable_area;
|
|
|
|
if (x != usable_area->x) {
|
|
return;
|
|
}
|
|
|
|
move_view_constrained(view, geometry, x, y);
|
|
view->geometry.y = y;
|
|
break;
|
|
}
|
|
} else {
|
|
move_view_constrained(view, geometry, x, y);
|
|
}
|
|
|
|
#ifdef HAVE_XWAYLAND
|
|
if (view->move != NULL) {
|
|
view->move(view, geometry->x, geometry->y);
|
|
}
|
|
#endif
|
|
|
|
refresh_border_geometry(view);
|
|
|
|
if (!hikari_view_is_hidden(view)) {
|
|
hikari_view_damage_whole(view);
|
|
hikari_indicator_damage(&hikari_server.indicator, view);
|
|
}
|
|
}
|
|
|
|
static void
|
|
increase_group_visiblity(struct hikari_view *view)
|
|
{
|
|
assert(hikari_view_is_forced(view) ? hikari_view_is_hidden(view)
|
|
: !hikari_view_is_hidden(view));
|
|
|
|
struct hikari_group *group = view->group;
|
|
|
|
if (wl_list_empty(&group->visible_views)) {
|
|
wl_list_insert(
|
|
&hikari_server.visible_groups, &group->visible_server_groups);
|
|
}
|
|
|
|
wl_list_init(&view->visible_group_views);
|
|
}
|
|
|
|
static void
|
|
decrease_group_visibility(struct hikari_view *view)
|
|
{
|
|
struct hikari_group *group = view->group;
|
|
|
|
wl_list_remove(&view->visible_group_views);
|
|
|
|
if (wl_list_empty(&group->visible_views)) {
|
|
wl_list_remove(&group->visible_server_groups);
|
|
}
|
|
}
|
|
|
|
static void
|
|
hide(struct hikari_view *view)
|
|
{
|
|
decrease_group_visibility(view);
|
|
|
|
wl_list_remove(&view->workspace_views);
|
|
wl_list_init(&view->workspace_views);
|
|
|
|
wl_list_remove(&view->visible_server_views);
|
|
wl_list_init(&view->visible_server_views);
|
|
|
|
hikari_view_set_hidden(view);
|
|
}
|
|
|
|
static void
|
|
detach_from_group(struct hikari_view *view)
|
|
{
|
|
struct hikari_group *group = view->group;
|
|
|
|
wl_list_remove(&view->group_views);
|
|
wl_list_init(&view->group_views);
|
|
|
|
if (wl_list_empty(&group->views)) {
|
|
hikari_group_fini(group);
|
|
hikari_free(group);
|
|
}
|
|
}
|
|
|
|
static void
|
|
remove_from_group(struct hikari_view *view)
|
|
{
|
|
assert(!hikari_view_is_hidden(view));
|
|
|
|
if (!hikari_view_is_hidden(view)) {
|
|
decrease_group_visibility(view);
|
|
}
|
|
|
|
detach_from_group(view);
|
|
}
|
|
|
|
static void
|
|
cancel_tile(struct hikari_view *view)
|
|
{
|
|
if (hikari_view_is_tiling(view)) {
|
|
struct hikari_tile *tile = view->pending_operation.tile;
|
|
|
|
hikari_tile_detach(tile);
|
|
hikari_free(tile);
|
|
view->pending_operation.tile = NULL;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
guarded_resize(struct hikari_view *view,
|
|
struct hikari_operation *op,
|
|
void (*f)(struct hikari_view *, struct hikari_operation *))
|
|
{
|
|
op->serial = view->resize(view, op->geometry.width, op->geometry.height);
|
|
|
|
if (op->serial == 0) {
|
|
f(view, op);
|
|
} else {
|
|
hikari_view_set_dirty(view);
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
resize(struct hikari_view *view,
|
|
struct hikari_operation *op,
|
|
void (*f)(struct hikari_view *, struct hikari_operation *))
|
|
{
|
|
#ifdef HAVE_XWAYLAND
|
|
if (view->move_resize != NULL) {
|
|
op->serial = 0;
|
|
view->move_resize(view,
|
|
op->geometry.x,
|
|
op->geometry.y,
|
|
op->geometry.width,
|
|
op->geometry.height);
|
|
|
|
hikari_view_set_dirty(view);
|
|
} else {
|
|
guarded_resize(view, op, f);
|
|
}
|
|
#else
|
|
guarded_resize(view, op, f);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
commit_pending_geometry(
|
|
struct hikari_view *view, struct wlr_box *pending_geometry)
|
|
{
|
|
hikari_view_refresh_geometry(view, pending_geometry);
|
|
|
|
hikari_indicator_damage(&hikari_server.indicator, view);
|
|
hikari_view_damage_whole(view);
|
|
}
|
|
|
|
static void
|
|
commit_pending_operation(
|
|
struct hikari_view *view, struct hikari_operation *operation)
|
|
{
|
|
if (!hikari_view_is_hidden(view)) {
|
|
if (!hikari_view_is_forced(view)) {
|
|
raise_view(view);
|
|
} else {
|
|
move_to_top(view);
|
|
}
|
|
|
|
commit_pending_geometry(view, &operation->geometry);
|
|
|
|
if (operation->center) {
|
|
hikari_view_center_cursor(view);
|
|
hikari_server_cursor_focus();
|
|
}
|
|
} else {
|
|
hikari_view_refresh_geometry(view, &operation->geometry);
|
|
|
|
if (hikari_sheet_is_visible(view->sheet)) {
|
|
if (!hikari_view_is_forced(view)) {
|
|
hikari_view_show(view);
|
|
} else {
|
|
raise_view(view);
|
|
}
|
|
|
|
if (operation->center) {
|
|
hikari_view_center_cursor(view);
|
|
hikari_server_cursor_focus();
|
|
}
|
|
} else {
|
|
if (!hikari_view_is_forced(view)) {
|
|
move_to_top(view);
|
|
} else {
|
|
raise_view(view);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
commit_reset(struct hikari_view *view, struct hikari_operation *operation)
|
|
{
|
|
hikari_indicator_damage(&hikari_server.indicator, view);
|
|
|
|
if (hikari_view_is_tiled(view)) {
|
|
assert(!hikari_tile_is_attached(view->tile));
|
|
hikari_free(view->tile);
|
|
view->tile = NULL;
|
|
}
|
|
|
|
if (hikari_view_is_maximized(view)) {
|
|
hikari_maximized_state_destroy(view->maximized_state);
|
|
view->maximized_state = NULL;
|
|
|
|
if (!view->use_csd) {
|
|
if (view == hikari_server.workspace->focus_view) {
|
|
view->border.state = HIKARI_BORDER_ACTIVE;
|
|
} else {
|
|
view->border.state = HIKARI_BORDER_INACTIVE;
|
|
}
|
|
}
|
|
}
|
|
|
|
commit_pending_operation(view, operation);
|
|
}
|
|
|
|
static void
|
|
queue_reset(struct hikari_view *view, bool center)
|
|
{
|
|
struct wlr_box *view_geometry = hikari_view_geometry(view);
|
|
struct hikari_operation *op = &view->pending_operation;
|
|
struct hikari_tile *tile = view->tile;
|
|
struct wlr_box *geometry = &view->geometry;
|
|
|
|
cancel_tile(view);
|
|
|
|
if (hikari_view_is_tiled(view) && hikari_tile_is_attached(tile)) {
|
|
hikari_tile_detach(tile);
|
|
}
|
|
|
|
op->type = HIKARI_OPERATION_TYPE_RESET;
|
|
op->center = center;
|
|
memcpy(&op->geometry, geometry, sizeof(struct wlr_box));
|
|
|
|
if (view_geometry->width == geometry->width &&
|
|
view_geometry->height == geometry->height) {
|
|
hikari_view_set_dirty(view);
|
|
hikari_view_commit_pending_operation(view, view_geometry);
|
|
} else {
|
|
resize(view, op, commit_reset);
|
|
}
|
|
|
|
assert(
|
|
hikari_view_is_tiled(view) ? !hikari_tile_is_attached(view->tile) : true);
|
|
}
|
|
|
|
static void
|
|
clear_focus(struct hikari_view *view)
|
|
{
|
|
if (hikari_view_is_focus_view(view)) {
|
|
if (hikari_view_has_focus(view)) {
|
|
assert(!hikari_server_in_lock_mode());
|
|
|
|
if (!hikari_server_in_normal_mode()) {
|
|
hikari_server_enter_normal_mode(NULL);
|
|
}
|
|
|
|
hikari_workspace_focus_view(hikari_server.workspace, NULL);
|
|
} else {
|
|
view->sheet->workspace->focus_view = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_view_init(
|
|
struct hikari_view *view, bool child, struct hikari_workspace *workspace)
|
|
{
|
|
#if !defined(NDEBUG)
|
|
printf("VIEW INIT %p\n", view);
|
|
#endif
|
|
view->flags = hikari_view_hidden_flag;
|
|
view->border.state = HIKARI_BORDER_INACTIVE;
|
|
view->sheet = NULL;
|
|
view->mark = NULL;
|
|
view->surface = NULL;
|
|
view->maximized_state = NULL;
|
|
view->output = NULL;
|
|
view->group = NULL;
|
|
view->title = NULL;
|
|
view->tile = NULL;
|
|
view->id = NULL;
|
|
view->use_csd = false;
|
|
view->child = child;
|
|
view->current_geometry = &view->geometry;
|
|
view->current_unmaximized_geometry = &view->geometry;
|
|
|
|
hikari_view_unset_dirty(view);
|
|
view->pending_operation.tile = NULL;
|
|
|
|
wl_list_init(&view->children);
|
|
}
|
|
|
|
void
|
|
hikari_view_fini(struct hikari_view *view)
|
|
{
|
|
assert(hikari_view_is_hidden(view));
|
|
assert(!hikari_view_is_mapped(view));
|
|
assert(!hikari_view_is_forced(view));
|
|
|
|
#if !defined(NDEBUG)
|
|
printf("DESTROY VIEW %p\n", view);
|
|
#endif
|
|
|
|
hikari_free(view->title);
|
|
hikari_free(view->id);
|
|
|
|
if (view->group != NULL) {
|
|
detach_from_group(view);
|
|
}
|
|
|
|
if (view->sheet != NULL) {
|
|
wl_list_remove(&view->sheet_views);
|
|
wl_list_remove(&view->output_views);
|
|
}
|
|
|
|
if (view->maximized_state != NULL) {
|
|
hikari_maximized_state_destroy(view->maximized_state);
|
|
}
|
|
|
|
if (view->mark != NULL) {
|
|
hikari_mark_clear(view->mark);
|
|
}
|
|
|
|
if (hikari_view_is_tiled(view)) {
|
|
struct hikari_tile *tile = view->tile;
|
|
hikari_tile_detach(tile);
|
|
hikari_free(tile);
|
|
view->tile = NULL;
|
|
}
|
|
|
|
cancel_tile(view);
|
|
}
|
|
|
|
void
|
|
hikari_view_set_title(struct hikari_view *view, const char *title)
|
|
{
|
|
hikari_free(view->title);
|
|
|
|
if (title != NULL) {
|
|
view->title = hikari_malloc(strlen(title) + 1);
|
|
strcpy(view->title, title);
|
|
|
|
if (hikari_server.workspace->focus_view == view) {
|
|
assert(!hikari_view_is_hidden(view));
|
|
struct hikari_output *output = view->output;
|
|
hikari_indicator_update_title(&hikari_server.indicator, output, title);
|
|
}
|
|
} else {
|
|
view->title = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_app_id(struct hikari_view *view, const char *id)
|
|
{
|
|
assert(view->id == NULL);
|
|
assert(id != NULL);
|
|
|
|
view->id = hikari_malloc(strlen(id) + 1);
|
|
|
|
strcpy(view->id, id);
|
|
}
|
|
|
|
struct hikari_damage_data {
|
|
struct wlr_box *geometry;
|
|
struct wlr_surface *surface;
|
|
|
|
struct hikari_view *view;
|
|
struct hikari_output *output;
|
|
|
|
bool whole;
|
|
};
|
|
|
|
static void
|
|
damage_whole_surface(struct wlr_surface *surface, int sx, int sy, void *data)
|
|
{
|
|
struct hikari_damage_data *damage_data = data;
|
|
struct hikari_output *output = damage_data->output;
|
|
struct hikari_view *view = damage_data->view;
|
|
|
|
if (view->surface == surface) {
|
|
hikari_view_damage_border(view);
|
|
} else {
|
|
struct wlr_box geometry;
|
|
memcpy(&geometry, damage_data->geometry, sizeof(struct wlr_box));
|
|
|
|
geometry.x += sx;
|
|
geometry.y += sy;
|
|
geometry.width = surface->current.width;
|
|
geometry.height = surface->current.height;
|
|
|
|
hikari_output_add_damage(output, &geometry);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_view_damage_whole(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
|
|
struct hikari_output *output = view->output;
|
|
|
|
// TODO I know, this needs to be done A LOT better
|
|
if (view->use_csd) {
|
|
hikari_output_damage_whole(output);
|
|
return;
|
|
}
|
|
|
|
struct hikari_damage_data damage_data;
|
|
|
|
damage_data.geometry = hikari_view_geometry(view);
|
|
damage_data.output = output;
|
|
damage_data.view = view;
|
|
|
|
hikari_node_for_each_surface(
|
|
(struct hikari_node *)view, damage_whole_surface, &damage_data);
|
|
}
|
|
|
|
static struct wlr_box *
|
|
refresh_unmaximized_geometry(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
|
|
if (hikari_view_is_tiled(view)) {
|
|
return &view->tile->view_geometry;
|
|
} else {
|
|
return &view->geometry;
|
|
}
|
|
}
|
|
|
|
static struct wlr_box *
|
|
refresh_geometry(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
|
|
if (view->maximized_state != NULL) {
|
|
return &view->maximized_state->geometry;
|
|
} else {
|
|
return refresh_unmaximized_geometry(view);
|
|
}
|
|
}
|
|
|
|
static inline int
|
|
constrain_size(int min, int max, int value)
|
|
{
|
|
if (value > max) {
|
|
return max;
|
|
} else if (value < min) {
|
|
return min;
|
|
} else {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
static void
|
|
commit_resize(struct hikari_view *view, struct hikari_operation *operation)
|
|
{
|
|
commit_pending_operation(view, operation);
|
|
}
|
|
|
|
static void
|
|
queue_resize(struct hikari_view *view,
|
|
struct wlr_box *geometry,
|
|
int requested_x,
|
|
int requested_y,
|
|
int requested_width,
|
|
int requested_height)
|
|
{
|
|
struct hikari_operation *op = &view->pending_operation;
|
|
|
|
int min_width;
|
|
int min_height;
|
|
int max_width;
|
|
int max_height;
|
|
|
|
int new_width;
|
|
int new_height;
|
|
|
|
if (view->maximized_state != NULL) {
|
|
switch (view->maximized_state->maximization) {
|
|
case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED:
|
|
return;
|
|
|
|
case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED:
|
|
view->constraints(
|
|
view, &min_width, &min_height, &max_width, &max_height);
|
|
new_width = constrain_size(min_width, max_width, requested_width);
|
|
new_height = geometry->height;
|
|
view->geometry.width = new_width;
|
|
break;
|
|
|
|
case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED:
|
|
view->constraints(
|
|
view, &min_width, &min_height, &max_width, &max_height);
|
|
new_width = geometry->width;
|
|
new_height = constrain_size(min_height, max_height, requested_height);
|
|
view->geometry.height = new_height;
|
|
break;
|
|
}
|
|
} else {
|
|
view->constraints(view, &min_width, &min_height, &max_width, &max_height);
|
|
new_width = constrain_size(min_width, max_width, requested_width);
|
|
new_height = constrain_size(min_height, max_height, requested_height);
|
|
}
|
|
|
|
struct hikari_output *output = view->output;
|
|
|
|
op->type = HIKARI_OPERATION_TYPE_RESIZE;
|
|
op->geometry.width = new_width;
|
|
op->geometry.height = new_height;
|
|
op->center = false;
|
|
|
|
hikari_geometry_constrain_relative(
|
|
&op->geometry, &output->usable_area, requested_x, requested_y);
|
|
|
|
resize(view, op, commit_resize);
|
|
}
|
|
|
|
void
|
|
hikari_view_resize(struct hikari_view *view, int dwidth, int dheight)
|
|
{
|
|
assert(view != NULL);
|
|
assert(view->resize != NULL);
|
|
assert(view->constraints != NULL);
|
|
|
|
if (hikari_view_is_dirty(view)) {
|
|
return;
|
|
}
|
|
|
|
struct wlr_box *geometry = hikari_view_geometry(view);
|
|
|
|
int requested_width = geometry->width + dwidth;
|
|
int requested_height = geometry->height + dheight;
|
|
|
|
queue_resize(view,
|
|
geometry,
|
|
geometry->x,
|
|
geometry->y,
|
|
requested_width,
|
|
requested_height);
|
|
}
|
|
|
|
void
|
|
hikari_view_resize_absolute(struct hikari_view *view, int width, int height)
|
|
{
|
|
assert(view != NULL);
|
|
assert(view->resize != NULL);
|
|
assert(view->constraints != NULL);
|
|
|
|
if (hikari_view_is_dirty(view)) {
|
|
return;
|
|
}
|
|
|
|
struct wlr_box *geometry = hikari_view_geometry(view);
|
|
queue_resize(view, geometry, geometry->x, geometry->y, width, height);
|
|
}
|
|
|
|
void
|
|
hikari_view_move_resize(
|
|
struct hikari_view *view, int x, int y, int width, int height)
|
|
{
|
|
assert(view != NULL);
|
|
assert(view->resize != NULL);
|
|
assert(view->constraints != NULL);
|
|
|
|
if (hikari_view_is_dirty(view)) {
|
|
return;
|
|
}
|
|
|
|
struct wlr_box *geometry = hikari_view_geometry(view);
|
|
|
|
int requested_x = geometry->x + x;
|
|
int requested_y = geometry->y + y;
|
|
int requested_width = geometry->width + width;
|
|
int requested_height = geometry->height + height;
|
|
|
|
queue_resize(view,
|
|
geometry,
|
|
requested_x,
|
|
requested_y,
|
|
requested_width,
|
|
requested_height);
|
|
}
|
|
|
|
void
|
|
hikari_view_move(struct hikari_view *view, int x, int y)
|
|
{
|
|
assert(view != NULL);
|
|
|
|
struct wlr_box *geometry = hikari_view_geometry(view);
|
|
|
|
move_view(view, geometry, geometry->x + x, geometry->y + y);
|
|
}
|
|
|
|
void
|
|
hikari_view_move_absolute(struct hikari_view *view, int x, int y)
|
|
{
|
|
assert(view != NULL);
|
|
|
|
struct wlr_box *geometry = hikari_view_geometry(view);
|
|
|
|
move_view(view, geometry, x, y);
|
|
}
|
|
|
|
#define MOVE(pos) \
|
|
void hikari_view_move_##pos(struct hikari_view *view) \
|
|
{ \
|
|
assert(view != NULL); \
|
|
\
|
|
struct hikari_output *output = view->output; \
|
|
struct wlr_box *usable_area = &output->usable_area; \
|
|
struct wlr_box *border_geometry = hikari_view_border_geometry(view); \
|
|
struct wlr_box *geometry = hikari_view_geometry(view); \
|
|
\
|
|
int x; \
|
|
int y; \
|
|
hikari_geometry_position_##pos(border_geometry, usable_area, &x, &y); \
|
|
\
|
|
move_view(view, geometry, x, y); \
|
|
}
|
|
|
|
MOVE(bottom_left)
|
|
MOVE(bottom_middle)
|
|
MOVE(bottom_right)
|
|
MOVE(center_left)
|
|
MOVE(center)
|
|
MOVE(center_right)
|
|
MOVE(top_left)
|
|
MOVE(top_middle)
|
|
MOVE(top_right)
|
|
#undef MOVE
|
|
|
|
static void
|
|
new_subsurface_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_view *view = wl_container_of(listener, view, new_subsurface);
|
|
|
|
struct wlr_subsurface *wlr_subsurface = data;
|
|
|
|
struct hikari_view_subsurface *view_subsurface =
|
|
hikari_malloc(sizeof(struct hikari_view_subsurface));
|
|
|
|
hikari_view_subsurface_init(view_subsurface, view, wlr_subsurface);
|
|
}
|
|
|
|
void
|
|
hikari_view_map(struct hikari_view *view, struct wlr_surface *surface)
|
|
{
|
|
assert(hikari_view_is_hidden(view));
|
|
assert(!hikari_view_is_unmanaged(view));
|
|
assert(!hikari_view_is_mapped(view));
|
|
|
|
struct hikari_sheet *sheet = view->sheet;
|
|
struct hikari_output *output = view->output;
|
|
struct hikari_group *group;
|
|
bool focus;
|
|
|
|
struct hikari_view_config *view_config =
|
|
hikari_configuration_resolve_view_config(hikari_configuration, view->id);
|
|
|
|
view->surface = surface;
|
|
|
|
view->new_subsurface.notify = new_subsurface_handler;
|
|
wl_signal_add(&surface->events.new_subsurface, &view->new_subsurface);
|
|
|
|
struct wlr_subsurface *wlr_subsurface;
|
|
wl_list_for_each (
|
|
wlr_subsurface, &surface->current.subsurfaces_below, current.link) {
|
|
struct hikari_view_subsurface *subsurface =
|
|
(struct hikari_view_subsurface *)malloc(
|
|
sizeof(struct hikari_view_subsurface));
|
|
hikari_view_subsurface_init(subsurface, view, wlr_subsurface);
|
|
}
|
|
wl_list_for_each (
|
|
wlr_subsurface, &surface->current.subsurfaces_above, current.link) {
|
|
struct hikari_view_subsurface *subsurface =
|
|
(struct hikari_view_subsurface *)malloc(
|
|
sizeof(struct hikari_view_subsurface));
|
|
hikari_view_subsurface_init(subsurface, view, wlr_subsurface);
|
|
}
|
|
|
|
if (view_config != NULL) {
|
|
struct hikari_mark *mark;
|
|
struct hikari_view_properties *properties =
|
|
hikari_view_config_resolve_properties(view_config, view->child);
|
|
|
|
assert(properties != NULL);
|
|
|
|
group = hikari_view_properties_resolve_group(properties, view->id);
|
|
mark = properties->mark;
|
|
|
|
if (mark != NULL && mark->view == NULL) {
|
|
hikari_mark_set(mark, view);
|
|
}
|
|
|
|
focus = properties->focus;
|
|
} else {
|
|
group = hikari_server_find_or_create_group(view->id);
|
|
focus = false;
|
|
}
|
|
|
|
view->group = group;
|
|
|
|
wl_list_insert(&sheet->views, &view->sheet_views);
|
|
wl_list_insert(&group->views, &view->group_views);
|
|
wl_list_insert(&output->views, &view->output_views);
|
|
|
|
if (!hikari_server_in_lock_mode() || hikari_view_is_public(view)) {
|
|
hikari_view_show(view);
|
|
|
|
if (focus) {
|
|
hikari_view_center_cursor(view);
|
|
}
|
|
|
|
hikari_server_cursor_focus();
|
|
} else {
|
|
hikari_view_set_forced(view);
|
|
increase_group_visiblity(view);
|
|
raise_view(view);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_view_unmap(struct hikari_view *view)
|
|
{
|
|
assert(!hikari_view_is_unmanaged(view));
|
|
assert(hikari_view_is_mapped(view));
|
|
|
|
wl_list_remove(&view->new_subsurface.link);
|
|
|
|
struct hikari_view_child *child, *child_temp;
|
|
wl_list_for_each_safe (child, child_temp, &view->children, link) {
|
|
struct hikari_view_subsurface *subsurface =
|
|
(struct hikari_view_subsurface *)child;
|
|
hikari_view_subsurface_fini(subsurface);
|
|
hikari_free(subsurface);
|
|
}
|
|
|
|
if (hikari_view_is_forced(view)) {
|
|
if (hikari_view_is_hidden(view)) {
|
|
hide(view);
|
|
} else {
|
|
hikari_view_damage_whole(view);
|
|
hikari_view_set_hidden(view);
|
|
}
|
|
|
|
hikari_view_unset_forced(view);
|
|
}
|
|
|
|
if (!hikari_view_is_hidden(view)) {
|
|
hikari_view_hide(view);
|
|
hikari_server_cursor_focus();
|
|
}
|
|
|
|
assert(hikari_view_is_hidden(view));
|
|
assert(!hikari_view_is_forced(view));
|
|
|
|
view->surface = NULL;
|
|
|
|
struct hikari_mark *mark = view->mark;
|
|
if (mark != NULL) {
|
|
hikari_mark_clear(mark);
|
|
}
|
|
|
|
detach_from_group(view);
|
|
view->group = NULL;
|
|
|
|
cancel_tile(view);
|
|
|
|
if (hikari_view_is_tiled(view)) {
|
|
struct wlr_box geometry;
|
|
struct hikari_tile *tile = view->tile;
|
|
|
|
memcpy(&geometry, hikari_view_geometry(view), sizeof(struct wlr_box));
|
|
|
|
if (hikari_tile_is_attached(tile)) {
|
|
hikari_tile_detach(tile);
|
|
}
|
|
|
|
hikari_free(tile);
|
|
view->tile = NULL;
|
|
|
|
hikari_view_refresh_geometry(view, &geometry);
|
|
}
|
|
|
|
wl_list_remove(&view->sheet_views);
|
|
wl_list_init(&view->sheet_views);
|
|
|
|
wl_list_remove(&view->output_views);
|
|
wl_list_init(&view->output_views);
|
|
|
|
hikari_view_unset_dirty(view);
|
|
|
|
assert(!hikari_view_is_tiling(view));
|
|
assert(!hikari_view_is_tiled(view));
|
|
}
|
|
|
|
void
|
|
hikari_view_show(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
assert(hikari_view_is_hidden(view));
|
|
assert(!hikari_view_is_forced(view));
|
|
|
|
#if !defined(NDEBUG)
|
|
printf("SHOW %p\n", view);
|
|
#endif
|
|
hikari_view_unset_hidden(view);
|
|
|
|
increase_group_visiblity(view);
|
|
|
|
raise_view(view);
|
|
|
|
hikari_view_damage_whole(view);
|
|
|
|
assert(is_first_view(view));
|
|
}
|
|
|
|
void
|
|
hikari_view_hide(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
assert(!hikari_view_is_hidden(view));
|
|
assert(!hikari_view_is_forced(view));
|
|
|
|
#if !defined(NDEBUG)
|
|
printf("HIDE %p\n", view);
|
|
#endif
|
|
|
|
clear_focus(view);
|
|
hide(view);
|
|
|
|
hikari_view_damage_whole(view);
|
|
}
|
|
|
|
void
|
|
hikari_view_raise(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
assert(!hikari_view_is_hidden(view));
|
|
|
|
if (is_first_view(view)) {
|
|
return;
|
|
}
|
|
|
|
raise_view(view);
|
|
hikari_view_damage_whole(view);
|
|
}
|
|
|
|
void
|
|
hikari_view_lower(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
assert(!hikari_view_is_hidden(view));
|
|
|
|
if (is_last_view(view)) {
|
|
return;
|
|
}
|
|
|
|
wl_list_remove(&view->sheet_views);
|
|
wl_list_insert(view->sheet->views.prev, &view->sheet_views);
|
|
|
|
wl_list_remove(&view->group_views);
|
|
wl_list_insert(view->group->views.prev, &view->group_views);
|
|
|
|
wl_list_remove(&view->output_views);
|
|
wl_list_insert(view->output->views.prev, &view->output_views);
|
|
|
|
wl_list_remove(&view->visible_group_views);
|
|
wl_list_insert(view->group->visible_views.prev, &view->visible_group_views);
|
|
|
|
wl_list_remove(&view->group->visible_server_groups);
|
|
wl_list_insert(
|
|
hikari_server.visible_groups.prev, &view->group->visible_server_groups);
|
|
|
|
wl_list_remove(&view->workspace_views);
|
|
wl_list_insert(view->sheet->workspace->views.prev, &view->workspace_views);
|
|
|
|
wl_list_remove(&view->visible_server_views);
|
|
wl_list_insert(hikari_server.visible_views.prev, &view->visible_server_views);
|
|
|
|
hikari_view_damage_whole(view);
|
|
}
|
|
|
|
static void
|
|
commit_tile(struct hikari_view *view, struct hikari_operation *operation)
|
|
{
|
|
if (view->maximized_state) {
|
|
hikari_maximized_state_destroy(view->maximized_state);
|
|
view->maximized_state = NULL;
|
|
if (!view->use_csd) {
|
|
view->border.state = HIKARI_BORDER_INACTIVE;
|
|
}
|
|
}
|
|
|
|
assert(hikari_tile_is_attached(operation->tile));
|
|
|
|
if (hikari_view_is_tiled(view)) {
|
|
struct hikari_tile *tile = view->tile;
|
|
|
|
assert(hikari_tile_is_attached(tile));
|
|
|
|
wl_list_remove(&tile->layout_tiles);
|
|
hikari_free(tile);
|
|
view->tile = NULL;
|
|
}
|
|
|
|
assert(!hikari_view_is_tiled(view));
|
|
assert(operation->tile != NULL);
|
|
view->tile = operation->tile;
|
|
operation->tile = NULL;
|
|
|
|
if (!hikari_view_is_hidden(view)) {
|
|
commit_pending_geometry(view, &operation->geometry);
|
|
if (operation->center) {
|
|
hikari_view_center_cursor(view);
|
|
}
|
|
hikari_server_cursor_focus();
|
|
} else {
|
|
hikari_view_refresh_geometry(view, &operation->geometry);
|
|
}
|
|
}
|
|
|
|
static void
|
|
queue_tile(struct hikari_view *view,
|
|
struct hikari_layout *layout,
|
|
struct hikari_tile *tile,
|
|
bool center)
|
|
{
|
|
assert(!hikari_view_is_dirty(view));
|
|
|
|
struct hikari_operation *op = &view->pending_operation;
|
|
|
|
struct wlr_box *current_geometry = hikari_view_geometry(view);
|
|
|
|
op->type = HIKARI_OPERATION_TYPE_TILE;
|
|
op->tile = tile;
|
|
op->geometry = tile->view_geometry;
|
|
op->center = center;
|
|
|
|
if (current_geometry->width == op->geometry.width &&
|
|
current_geometry->height == op->geometry.height) {
|
|
hikari_view_set_dirty(view);
|
|
|
|
hikari_view_commit_pending_operation(view, current_geometry);
|
|
} else {
|
|
resize(view, op, commit_tile);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_view_tile(
|
|
struct hikari_view *view, struct wlr_box *geometry, bool center)
|
|
{
|
|
assert(!hikari_view_is_dirty(view));
|
|
assert(hikari_view_is_tileable(view));
|
|
|
|
struct hikari_layout *layout = view->sheet->workspace->sheet->layout;
|
|
|
|
struct hikari_tile *tile = hikari_malloc(sizeof(struct hikari_tile));
|
|
hikari_tile_init(tile, view, layout, geometry, geometry);
|
|
|
|
queue_tile(view, layout, tile, center);
|
|
|
|
wl_list_insert(layout->tiles.prev, &tile->layout_tiles);
|
|
}
|
|
|
|
static void
|
|
commit_full_maximize(
|
|
struct hikari_view *view, struct hikari_operation *operation)
|
|
{
|
|
if (!view->maximized_state) {
|
|
view->maximized_state =
|
|
hikari_malloc(sizeof(struct hikari_maximized_state));
|
|
}
|
|
|
|
view->maximized_state->maximization = HIKARI_MAXIMIZATION_FULLY_MAXIMIZED;
|
|
view->maximized_state->geometry = operation->geometry;
|
|
|
|
if (!view->use_csd) {
|
|
view->border.state = HIKARI_BORDER_NONE;
|
|
}
|
|
|
|
commit_pending_operation(view, operation);
|
|
}
|
|
|
|
static void
|
|
queue_full_maximize(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
assert(!hikari_view_is_hidden(view));
|
|
|
|
struct hikari_operation *op = &view->pending_operation;
|
|
struct hikari_output *output = view->output;
|
|
|
|
op->type = HIKARI_OPERATION_TYPE_FULL_MAXIMIZE;
|
|
op->geometry = output->usable_area;
|
|
op->center = true;
|
|
|
|
resize(view, op, commit_full_maximize);
|
|
}
|
|
|
|
static void
|
|
commit_unmaximize(struct hikari_view *view, struct hikari_operation *operation)
|
|
{
|
|
hikari_view_damage_whole(view);
|
|
|
|
hikari_free(view->maximized_state);
|
|
view->maximized_state = NULL;
|
|
|
|
if (!view->use_csd) {
|
|
view->border.state = HIKARI_BORDER_ACTIVE;
|
|
}
|
|
|
|
commit_pending_operation(view, operation);
|
|
}
|
|
|
|
static void
|
|
queue_unmaximize(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
assert(!hikari_view_is_hidden(view));
|
|
|
|
struct hikari_operation *op = &view->pending_operation;
|
|
|
|
op->type = HIKARI_OPERATION_TYPE_UNMAXIMIZE;
|
|
op->center = true;
|
|
|
|
if (view->tile != NULL) {
|
|
op->geometry = view->tile->view_geometry;
|
|
} else {
|
|
op->geometry = view->geometry;
|
|
}
|
|
|
|
resize(view, op, commit_unmaximize);
|
|
}
|
|
|
|
void
|
|
hikari_view_toggle_full_maximize(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
assert(!hikari_view_is_hidden(view));
|
|
|
|
if (hikari_view_is_dirty(view)) {
|
|
return;
|
|
}
|
|
|
|
if (hikari_view_is_fully_maximized(view)) {
|
|
queue_unmaximize(view);
|
|
} else {
|
|
queue_full_maximize(view);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_view_toggle_public(struct hikari_view *view)
|
|
{
|
|
if (hikari_view_is_public(view)) {
|
|
hikari_view_unset_public(view);
|
|
} else {
|
|
hikari_view_set_public(view);
|
|
}
|
|
}
|
|
|
|
static void
|
|
commit_horizontal_maximize(
|
|
struct hikari_view *view, struct hikari_operation *operation)
|
|
{
|
|
if (!view->maximized_state) {
|
|
view->maximized_state =
|
|
hikari_malloc(sizeof(struct hikari_maximized_state));
|
|
} else {
|
|
switch (view->maximized_state->maximization) {
|
|
case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED:
|
|
commit_full_maximize(view, operation);
|
|
return;
|
|
|
|
case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED:
|
|
if (!view->use_csd) {
|
|
view->border.state = HIKARI_BORDER_INACTIVE;
|
|
}
|
|
break;
|
|
|
|
case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
view->maximized_state->maximization =
|
|
HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED;
|
|
view->maximized_state->geometry = operation->geometry;
|
|
|
|
commit_pending_operation(view, operation);
|
|
}
|
|
|
|
static void
|
|
queue_horizontal_maximize(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
assert(!hikari_view_is_hidden(view));
|
|
|
|
struct hikari_operation *op = &view->pending_operation;
|
|
struct hikari_output *output = view->output;
|
|
|
|
struct wlr_box *geometry = view->current_unmaximized_geometry;
|
|
|
|
op->type = HIKARI_OPERATION_TYPE_HORIZONTAL_MAXIMIZE;
|
|
op->geometry.x = output->usable_area.x;
|
|
op->geometry.y = geometry->y;
|
|
op->geometry.height = geometry->height;
|
|
op->geometry.width = output->usable_area.width;
|
|
op->center = true;
|
|
|
|
resize(view, op, commit_horizontal_maximize);
|
|
}
|
|
|
|
static void
|
|
commit_vertical_maximize(
|
|
struct hikari_view *view, struct hikari_operation *operation)
|
|
{
|
|
if (!view->maximized_state) {
|
|
view->maximized_state =
|
|
hikari_malloc(sizeof(struct hikari_maximized_state));
|
|
} else {
|
|
switch (view->maximized_state->maximization) {
|
|
case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED:
|
|
commit_full_maximize(view, operation);
|
|
return;
|
|
|
|
case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED:
|
|
if (!view->use_csd) {
|
|
view->border.state = HIKARI_BORDER_INACTIVE;
|
|
}
|
|
break;
|
|
|
|
case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
view->maximized_state->maximization =
|
|
HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED;
|
|
view->maximized_state->geometry = operation->geometry;
|
|
|
|
commit_pending_operation(view, operation);
|
|
}
|
|
|
|
static void
|
|
queue_vertical_maximize(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
assert(!hikari_view_is_hidden(view));
|
|
|
|
struct hikari_operation *op = &view->pending_operation;
|
|
struct hikari_output *output = view->output;
|
|
|
|
struct wlr_box *geometry = view->current_unmaximized_geometry;
|
|
|
|
op->type = HIKARI_OPERATION_TYPE_VERTICAL_MAXIMIZE;
|
|
op->geometry.x = geometry->x;
|
|
op->geometry.y = output->usable_area.y;
|
|
op->geometry.height = output->usable_area.height;
|
|
op->geometry.width = geometry->width;
|
|
op->center = true;
|
|
|
|
resize(view, op, commit_vertical_maximize);
|
|
}
|
|
|
|
void
|
|
hikari_view_toggle_vertical_maximize(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
assert(!hikari_view_is_hidden(view));
|
|
|
|
if (hikari_view_is_dirty(view)) {
|
|
return;
|
|
}
|
|
|
|
if (view->maximized_state != NULL) {
|
|
switch (view->maximized_state->maximization) {
|
|
case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED:
|
|
queue_horizontal_maximize(view);
|
|
break;
|
|
|
|
case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED:
|
|
queue_unmaximize(view);
|
|
break;
|
|
|
|
case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED:
|
|
queue_full_maximize(view);
|
|
break;
|
|
}
|
|
} else {
|
|
queue_vertical_maximize(view);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_view_toggle_horizontal_maximize(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
assert(!hikari_view_is_hidden(view));
|
|
|
|
if (view->maximized_state != NULL) {
|
|
switch (view->maximized_state->maximization) {
|
|
case HIKARI_MAXIMIZATION_FULLY_MAXIMIZED:
|
|
queue_vertical_maximize(view);
|
|
break;
|
|
|
|
case HIKARI_MAXIMIZATION_VERTICALLY_MAXIMIZED:
|
|
queue_full_maximize(view);
|
|
break;
|
|
|
|
case HIKARI_MAXIMIZATION_HORIZONTALLY_MAXIMIZED:
|
|
queue_unmaximize(view);
|
|
break;
|
|
}
|
|
} else {
|
|
queue_horizontal_maximize(view);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_view_toggle_floating(struct hikari_view *view)
|
|
{
|
|
if (!hikari_view_is_floating(view)) {
|
|
if (hikari_view_is_tiled(view)) {
|
|
hikari_view_reset_geometry(view);
|
|
}
|
|
hikari_view_set_floating(view);
|
|
} else {
|
|
hikari_view_unset_floating(view);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_view_reset_geometry(struct hikari_view *view)
|
|
{
|
|
queue_reset(view, true);
|
|
}
|
|
|
|
void
|
|
hikari_view_evacuate(struct hikari_view *view, struct hikari_sheet *sheet)
|
|
{
|
|
#ifndef NDEBUG
|
|
printf("EVACUATE VIEW %p\n", view);
|
|
#endif
|
|
|
|
clear_focus(view);
|
|
|
|
view->output = sheet->workspace->output;
|
|
view->sheet = sheet;
|
|
|
|
if (!hikari_view_is_hidden(view)) {
|
|
if (hikari_view_is_forced(view)) {
|
|
move_to_top(view);
|
|
} else {
|
|
raise_view(view);
|
|
}
|
|
|
|
if (hikari_sheet_is_visible(sheet)) {
|
|
hikari_view_damage_whole(view);
|
|
hikari_indicator_damage(&hikari_server.indicator, view);
|
|
} else {
|
|
if (hikari_view_is_forced(view)) {
|
|
move_to_top(view);
|
|
} else {
|
|
hikari_view_hide(view);
|
|
}
|
|
}
|
|
} else {
|
|
if (hikari_view_is_forced(view)) {
|
|
raise_view(view);
|
|
} else {
|
|
move_to_top(view);
|
|
}
|
|
}
|
|
|
|
if (hikari_view_is_tiled(view) || hikari_view_is_maximized(view)) {
|
|
queue_reset(view, false);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_view_pin_to_sheet(struct hikari_view *view, struct hikari_sheet *sheet)
|
|
{
|
|
assert(view != NULL);
|
|
assert(sheet != NULL);
|
|
assert(sheet->workspace->output == view->output);
|
|
|
|
if (view->sheet == sheet) {
|
|
assert(!hikari_view_is_hidden(view));
|
|
|
|
if (view->sheet->workspace->sheet != sheet && sheet->nr != 0) {
|
|
hikari_view_hide(view);
|
|
hikari_server_cursor_focus();
|
|
|
|
move_to_top(view);
|
|
} else {
|
|
hikari_view_raise(view);
|
|
hikari_indicator_damage(&hikari_server.indicator, view);
|
|
}
|
|
} else {
|
|
if (hikari_sheet_is_visible(sheet)) {
|
|
place_visibly_above(view, sheet->workspace);
|
|
|
|
hikari_view_damage_whole(view);
|
|
hikari_indicator_damage(&hikari_server.indicator, view);
|
|
} else {
|
|
hikari_view_hide(view);
|
|
hikari_server_cursor_focus();
|
|
}
|
|
|
|
view->sheet = sheet;
|
|
|
|
if (hikari_view_is_tiled(view)) {
|
|
queue_reset(view, true);
|
|
} else {
|
|
move_to_top(view);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_view_center_cursor(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
|
|
struct hikari_output *output = view->output;
|
|
struct wlr_box *view_geometry = hikari_view_geometry(view);
|
|
|
|
struct wlr_box geometry;
|
|
hikari_geometry_constrain_size(
|
|
view_geometry, &output->usable_area, &geometry);
|
|
|
|
hikari_cursor_center(&hikari_server.cursor, output, &geometry);
|
|
}
|
|
|
|
void
|
|
hikari_view_top_left_cursor(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
|
|
struct wlr_box *geometry = hikari_view_geometry(view);
|
|
struct hikari_output *output = view->output;
|
|
|
|
int x = output->geometry.x + geometry->x;
|
|
int y = output->geometry.y + geometry->y;
|
|
|
|
hikari_cursor_warp(&hikari_server.cursor, x, y);
|
|
}
|
|
|
|
void
|
|
hikari_view_bottom_right_cursor(struct hikari_view *view)
|
|
{
|
|
assert(view != NULL);
|
|
|
|
struct wlr_box *geometry = hikari_view_geometry(view);
|
|
struct hikari_output *output = view->output;
|
|
|
|
int x = output->geometry.x + geometry->x + geometry->width;
|
|
int y = output->geometry.y + geometry->y + geometry->height;
|
|
|
|
hikari_cursor_warp(&hikari_server.cursor, x, y);
|
|
}
|
|
|
|
void
|
|
hikari_view_toggle_invisible(struct hikari_view *view)
|
|
{
|
|
if (hikari_view_is_invisible(view)) {
|
|
hikari_view_unset_invisible(view);
|
|
} else {
|
|
if (hikari_view_is_tiled(view)) {
|
|
hikari_view_reset_geometry(view);
|
|
}
|
|
hikari_view_set_invisible(view);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_view_group(struct hikari_view *view, struct hikari_group *group)
|
|
{
|
|
assert(view != NULL);
|
|
assert(group != NULL);
|
|
assert(view->group != NULL);
|
|
|
|
if (view->group == group) {
|
|
return;
|
|
}
|
|
|
|
remove_from_group(view);
|
|
view->group = group;
|
|
|
|
increase_group_visiblity(view);
|
|
|
|
raise_view(view);
|
|
|
|
hikari_view_damage_whole(view);
|
|
}
|
|
|
|
void
|
|
hikari_view_exchange(struct hikari_view *from, struct hikari_view *to)
|
|
{
|
|
assert(from != NULL);
|
|
assert(to != NULL);
|
|
|
|
if (hikari_view_is_dirty(from) || hikari_view_is_dirty(to)) {
|
|
return;
|
|
}
|
|
|
|
assert(from->tile != NULL);
|
|
assert(to->tile != NULL);
|
|
assert(to->tile->view->sheet == from->tile->view->sheet);
|
|
|
|
struct hikari_layout *layout = from->sheet->workspace->sheet->layout;
|
|
|
|
struct wlr_box *from_geometry = &from->tile->tile_geometry;
|
|
struct wlr_box *to_geometry = &to->tile->tile_geometry;
|
|
|
|
struct hikari_tile *from_tile = hikari_malloc(sizeof(struct hikari_tile));
|
|
struct hikari_tile *to_tile = hikari_malloc(sizeof(struct hikari_tile));
|
|
|
|
hikari_tile_init(from_tile, from, layout, to_geometry, to_geometry);
|
|
hikari_tile_init(to_tile, to, layout, from_geometry, from_geometry);
|
|
|
|
wl_list_insert(&from->tile->layout_tiles, &to_tile->layout_tiles);
|
|
wl_list_insert(&to->tile->layout_tiles, &from_tile->layout_tiles);
|
|
|
|
queue_tile(from, layout, from_tile, true);
|
|
queue_tile(to, layout, to_tile, false);
|
|
}
|
|
|
|
static void
|
|
destroy_subsurface_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_view_subsurface *view_subsurface =
|
|
wl_container_of(listener, view_subsurface, destroy);
|
|
|
|
hikari_view_subsurface_fini(view_subsurface);
|
|
|
|
hikari_free(view_subsurface);
|
|
}
|
|
|
|
void
|
|
hikari_view_subsurface_init(struct hikari_view_subsurface *view_subsurface,
|
|
struct hikari_view *parent,
|
|
struct wlr_subsurface *subsurface)
|
|
{
|
|
view_subsurface->subsurface = subsurface;
|
|
|
|
view_subsurface->destroy.notify = destroy_subsurface_handler;
|
|
wl_signal_add(
|
|
&subsurface->surface->events.destroy, &view_subsurface->destroy);
|
|
|
|
hikari_view_child_init(
|
|
(struct hikari_view_child *)view_subsurface, parent, subsurface->surface);
|
|
}
|
|
|
|
void
|
|
hikari_view_child_fini(struct hikari_view_child *view_child)
|
|
{
|
|
wl_list_remove(&view_child->link);
|
|
wl_list_remove(&view_child->commit.link);
|
|
wl_list_remove(&view_child->new_subsurface.link);
|
|
}
|
|
|
|
void
|
|
hikari_view_subsurface_fini(struct hikari_view_subsurface *view_subsurface)
|
|
{
|
|
hikari_view_child_fini(&view_subsurface->view_child);
|
|
wl_list_remove(&view_subsurface->destroy.link);
|
|
}
|
|
|
|
static void
|
|
damage_surface(struct wlr_surface *surface, int sx, int sy, void *data)
|
|
{
|
|
struct hikari_damage_data *damage_data = data;
|
|
struct hikari_output *output = damage_data->output;
|
|
|
|
if (damage_data->whole) {
|
|
damage_whole_surface(surface, sx, sy, data);
|
|
} else {
|
|
struct wlr_box *geometry = damage_data->geometry;
|
|
|
|
hikari_output_add_effective_surface_damage(
|
|
output, surface, geometry->x + sx, geometry->y + sy);
|
|
}
|
|
}
|
|
|
|
static void
|
|
damage_single_surface(struct wlr_surface *surface, int sx, int sy, void *data)
|
|
{
|
|
struct hikari_damage_data *damage_data = data;
|
|
|
|
if (damage_data->surface == surface) {
|
|
damage_surface(surface, sx, sy, data);
|
|
}
|
|
}
|
|
|
|
static void
|
|
commit_child_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_view_child *view_child =
|
|
wl_container_of(listener, view_child, commit);
|
|
|
|
struct hikari_view *parent = view_child->parent;
|
|
|
|
if (!hikari_view_is_hidden(parent)) {
|
|
struct wlr_surface *surface = view_child->surface;
|
|
|
|
hikari_view_damage_surface(parent, surface, false);
|
|
}
|
|
}
|
|
|
|
static void
|
|
view_subsurface_create(
|
|
struct wlr_subsurface *wlr_subsurface, struct hikari_view *parent)
|
|
{
|
|
struct hikari_view_subsurface *view_subsurface =
|
|
hikari_malloc(sizeof(struct hikari_view_subsurface));
|
|
|
|
hikari_view_subsurface_init(view_subsurface, parent, wlr_subsurface);
|
|
}
|
|
|
|
static void
|
|
new_subsurface_child_handler(struct wl_listener *listener, void *data)
|
|
{
|
|
struct hikari_view_child *view_child =
|
|
wl_container_of(listener, view_child, new_subsurface);
|
|
|
|
struct wlr_subsurface *wlr_subsurface = data;
|
|
|
|
view_subsurface_create(wlr_subsurface, view_child->parent);
|
|
}
|
|
|
|
void
|
|
hikari_view_child_init(struct hikari_view_child *view_child,
|
|
struct hikari_view *parent,
|
|
struct wlr_surface *surface)
|
|
{
|
|
view_child->parent = parent;
|
|
view_child->surface = surface;
|
|
|
|
view_child->new_subsurface.notify = new_subsurface_child_handler;
|
|
wl_signal_add(&surface->events.new_subsurface, &view_child->new_subsurface);
|
|
|
|
view_child->commit.notify = commit_child_handler;
|
|
wl_signal_add(&surface->events.commit, &view_child->commit);
|
|
|
|
wl_list_insert(&parent->children, &view_child->link);
|
|
|
|
struct wlr_subsurface *subsurface;
|
|
wl_list_for_each (
|
|
subsurface, &surface->current.subsurfaces_below, current.link) {
|
|
view_subsurface_create(subsurface, parent);
|
|
}
|
|
wl_list_for_each (
|
|
subsurface, &surface->current.subsurfaces_above, current.link) {
|
|
view_subsurface_create(subsurface, parent);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_view_damage_surface(
|
|
struct hikari_view *view, struct wlr_surface *surface, bool whole)
|
|
{
|
|
assert(view != NULL);
|
|
|
|
// TODO I know, this needs to be done A LOT better
|
|
if (view->use_csd) {
|
|
hikari_output_damage_whole(view->output);
|
|
return;
|
|
}
|
|
|
|
struct hikari_damage_data damage_data;
|
|
|
|
damage_data.geometry = hikari_view_geometry(view);
|
|
damage_data.output = view->output;
|
|
damage_data.surface = surface;
|
|
damage_data.whole = whole;
|
|
damage_data.view = view;
|
|
|
|
hikari_node_for_each_surface(
|
|
(struct hikari_node *)view, damage_single_surface, &damage_data);
|
|
}
|
|
|
|
void
|
|
hikari_view_refresh_geometry(struct hikari_view *view, struct wlr_box *geometry)
|
|
{
|
|
struct wlr_box *new_geometry = refresh_geometry(view);
|
|
|
|
memcpy(new_geometry, geometry, sizeof(struct wlr_box));
|
|
|
|
view->current_geometry = new_geometry;
|
|
view->current_unmaximized_geometry = refresh_unmaximized_geometry(view);
|
|
|
|
refresh_border_geometry(view);
|
|
}
|
|
|
|
static void
|
|
commit_operation(struct hikari_operation *operation, struct hikari_view *view)
|
|
{
|
|
switch (operation->type) {
|
|
case HIKARI_OPERATION_TYPE_RESIZE:
|
|
commit_resize(view, operation);
|
|
break;
|
|
|
|
case HIKARI_OPERATION_TYPE_RESET:
|
|
commit_reset(view, operation);
|
|
break;
|
|
|
|
case HIKARI_OPERATION_TYPE_UNMAXIMIZE:
|
|
commit_unmaximize(view, operation);
|
|
break;
|
|
|
|
case HIKARI_OPERATION_TYPE_FULL_MAXIMIZE:
|
|
commit_full_maximize(view, operation);
|
|
break;
|
|
|
|
case HIKARI_OPERATION_TYPE_VERTICAL_MAXIMIZE:
|
|
commit_vertical_maximize(view, operation);
|
|
break;
|
|
|
|
case HIKARI_OPERATION_TYPE_HORIZONTAL_MAXIMIZE:
|
|
commit_horizontal_maximize(view, operation);
|
|
break;
|
|
|
|
case HIKARI_OPERATION_TYPE_TILE:
|
|
commit_tile(view, operation);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_view_commit_pending_operation(
|
|
struct hikari_view *view, struct wlr_box *geometry)
|
|
{
|
|
assert(view != NULL);
|
|
assert(hikari_view_is_dirty(view));
|
|
|
|
view->pending_operation.geometry.width = geometry->width;
|
|
view->pending_operation.geometry.height = geometry->height;
|
|
|
|
hikari_indicator_damage(&hikari_server.indicator, view);
|
|
hikari_view_damage_whole(view);
|
|
|
|
commit_operation(&view->pending_operation, view);
|
|
hikari_view_unset_dirty(view);
|
|
}
|
|
|
|
void
|
|
hikari_view_activate(struct hikari_view *view, bool active)
|
|
{
|
|
assert(view != NULL);
|
|
|
|
if (view->activate) {
|
|
if (view->border.state != HIKARI_BORDER_NONE) {
|
|
view->border.state =
|
|
active ? HIKARI_BORDER_ACTIVE : HIKARI_BORDER_INACTIVE;
|
|
}
|
|
view->activate(view, active);
|
|
}
|
|
}
|
|
|
|
static void
|
|
migrate_view(struct hikari_view *view, struct hikari_sheet *sheet, bool center)
|
|
{
|
|
assert(hikari_view_is_hidden(view));
|
|
|
|
view->output = sheet->workspace->output;
|
|
view->sheet = sheet;
|
|
|
|
move_to_top(view);
|
|
|
|
queue_reset(view, center);
|
|
}
|
|
|
|
void
|
|
hikari_view_migrate(struct hikari_view *view,
|
|
struct hikari_sheet *sheet,
|
|
int x,
|
|
int y,
|
|
bool center)
|
|
{
|
|
struct hikari_output *output = sheet->workspace->output;
|
|
struct wlr_box *view_geometry = hikari_view_geometry(view);
|
|
|
|
hikari_indicator_damage(&hikari_server.indicator, view);
|
|
hikari_view_damage_whole(view);
|
|
|
|
// only remove view from lists and do not make it lose focus by calling
|
|
// `hikari_view_hide`.
|
|
hide(view);
|
|
|
|
hikari_geometry_constrain_relative(
|
|
&view->geometry, &output->usable_area, x, y);
|
|
hikari_geometry_constrain_relative(view_geometry, &output->usable_area, x, y);
|
|
|
|
migrate_view(view, sheet, center);
|
|
|
|
#ifdef HAVE_XWAYLAND
|
|
if (view->move != NULL) {
|
|
view->move(view, view_geometry->x, view_geometry->y);
|
|
}
|
|
#endif
|
|
|
|
if (hikari_view_is_hidden(view)) {
|
|
hikari_view_show(view);
|
|
}
|
|
}
|
|
|
|
void
|
|
hikari_view_configure(struct hikari_view *view,
|
|
const char *app_id,
|
|
struct hikari_view_config *view_config)
|
|
{
|
|
assert(view->id == NULL);
|
|
|
|
struct hikari_sheet *sheet;
|
|
struct hikari_output *output;
|
|
struct wlr_box *geometry = &view->geometry;
|
|
int x, y;
|
|
bool invisible, floating, publicview;
|
|
|
|
set_app_id(view, app_id);
|
|
|
|
if (view_config != NULL) {
|
|
struct hikari_view_properties *properties =
|
|
hikari_view_config_resolve_properties(view_config, view->child);
|
|
|
|
sheet = hikari_view_properties_resolve_sheet(properties);
|
|
output = sheet->workspace->output;
|
|
|
|
invisible = properties->invisible;
|
|
floating = properties->floating;
|
|
publicview = properties->publicview;
|
|
|
|
hikari_view_properties_resolve_position(properties, view, &x, &y);
|
|
} else {
|
|
sheet = hikari_server.workspace->sheet;
|
|
output = sheet->workspace->output;
|
|
|
|
invisible = false;
|
|
floating = false;
|
|
publicview = false;
|
|
|
|
x = hikari_server.cursor.wlr_cursor->x - output->geometry.x;
|
|
y = hikari_server.cursor.wlr_cursor->y - output->geometry.y;
|
|
}
|
|
|
|
view->sheet = sheet;
|
|
view->output = output;
|
|
|
|
wl_list_init(&view->workspace_views);
|
|
wl_list_init(&view->visible_server_views);
|
|
|
|
if (invisible) {
|
|
hikari_view_set_invisible(view);
|
|
}
|
|
|
|
if (floating) {
|
|
hikari_view_set_floating(view);
|
|
}
|
|
|
|
if (publicview) {
|
|
hikari_view_set_public(view);
|
|
}
|
|
|
|
hikari_geometry_constrain_absolute(geometry, &output->usable_area, x, y);
|
|
hikari_view_refresh_geometry(view, geometry);
|
|
}
|