diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..cd9bd8d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,17 @@ +--- +name: Bug report +about: Something in dwl isn't working correctly +title: '' +labels: 'A: bug' +assignees: '' + +--- + +## Info +dwl version: +wlroots version: +## Description + diff --git a/.github/ISSUE_TEMPLATE/enhancement-idea.md b/.github/ISSUE_TEMPLATE/enhancement-idea.md new file mode 100644 index 0000000..0ac096d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement-idea.md @@ -0,0 +1,10 @@ +--- +name: Enhancement idea +about: Suggest a feature or improvement +title: '' +labels: 'A: enhancement' +assignees: '' + +--- + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f576a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.cache +/.vscode \ No newline at end of file diff --git a/IM.h b/IM.h new file mode 100644 index 0000000..0cb842d --- /dev/null +++ b/IM.h @@ -0,0 +1,704 @@ +#ifdef IM +#include +#include +#include + +/** + * The relay structure manages the relationship between text-input and + * input_method interfaces on a given seat. Multiple text-input interfaces may + * be bound to a relay, but at most one will be focused (receiving events) at + * a time. At most one input-method interface may be bound to the seat. The + * relay manages life cycle of both sides. When both sides are present and + * focused, the relay passes messages between them. + * + * Text input focus is a subset of keyboard focus - if the text-input is + * in the focused state, wl_keyboard sent an enter as well. However, having + * wl_keyboard focused doesn't mean that text-input will be focused. + */ +struct dwl_input_method_relay { + struct wl_list text_inputs; // dwl_text_input::link + struct wlr_input_method_v2 *input_method; // doesn't have to be present + + struct dwl_input_popup *popup; + + struct wl_listener text_input_new; + + struct wl_listener input_method_new; + struct wl_listener input_method_commit; + struct wl_listener input_method_destroy; + struct wl_listener input_method_new_popup_surface; + struct wl_listener input_method_grab_keyboard; + struct wl_listener input_method_keyboard_grab_destroy; +}; + +struct dwl_text_input { + struct dwl_input_method_relay *relay; + + struct wlr_text_input_v3 *input; + // The surface getting seat's focus. Stored for when text-input cannot + // be sent an enter event immediately after getting focus, e.g. when + // there's no input method available. Cleared once text-input is entered. + struct wlr_surface *pending_focused_surface; + + struct wl_list link; + + struct wl_listener pending_focused_surface_destroy; + + struct wl_listener text_input_enable; + //struct wl_listener text_input_commit; + struct wl_listener text_input_disable; + struct wl_listener text_input_destroy; +}; + + +struct dwl_input_popup { + struct dwl_input_method_relay *relay; + struct wlr_input_popup_surface_v2 *popup_surface; + + //struct wlr_scene_node *scene; + struct wlr_scene_tree *scene; + //struct wlr_scene_node *scene_surface; + struct wlr_scene_tree *scene_surface; + + int x, y; + bool visible; + + + struct wl_listener popup_map; + struct wl_listener popup_unmap; + struct wl_listener popup_destroy; + struct wl_listener popup_surface_commit; + //struct wl_listener focused_surface_unmap; +}; + + +void dwl_input_method_relay_init(struct dwl_input_method_relay *relay); +// Updates currently focused surface. Surface must belong to the same +// seat. +void dwl_input_method_relay_set_focus(struct dwl_input_method_relay *relay, + struct wlr_surface *surface); +struct dwl_text_input *dwl_text_input_create( + struct dwl_input_method_relay *relay, + struct wlr_text_input_v3 *text_input); +static void handle_im_grab_keyboard(struct wl_listener *listener, void *data); +static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, + void *data); +static void input_popup_update(struct dwl_input_popup *popup); + +struct wlr_input_method_manager_v2 *input_method_manager; +struct wlr_text_input_manager_v3 *text_input_manager; +struct dwl_input_method_relay *input_relay; + +/** + * Get keyboard grab of the seat from sway_keyboard if we should forward events + * to it. + * + * Returns NULL if the keyboard is not grabbed by an input method, + * or if event is from virtual keyboard of the same client as grab. + * TODO: see https://github.com/swaywm/wlroots/issues/2322 + */ +static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab(Keyboard* kb) { + struct wlr_input_method_v2 *input_method = input_relay->input_method; + struct wlr_virtual_keyboard_v1 *virtual_keyboard = + wlr_input_device_get_virtual_keyboard(&kb->wlr_keyboard->base); + if (!input_method || !input_method->keyboard_grab || (virtual_keyboard && + wl_resource_get_client(virtual_keyboard->resource) == + wl_resource_get_client(input_method->keyboard_grab->resource))) { + if (!input_method){ + wlr_log(WLR_DEBUG, "keypress keyboard_get_im_grab return NULL:no input_method"); + } else if (!input_method->keyboard_grab){ + wlr_log(WLR_DEBUG, "keypress keyboard_get_im_grab return NULL:no input_method->keyboard_grab"); + } + + if (virtual_keyboard) { + wlr_log(WLR_DEBUG, "keypress keyboard_get_im_grab return NULL:virtual_keyboard"); + } + + return NULL; + } + return input_method->keyboard_grab; +} + +static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) { + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + input_method_grab_keyboard); + struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; + + // send modifier state to grab + struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(seat); + if (active_keyboard){ + wlr_log(WLR_INFO,"im_grab_keyboard"); + wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab,active_keyboard); + wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab, &active_keyboard->modifiers); + } + else + wlr_log(WLR_INFO,"im_grab_keyboard but no active keyboard"); + + + wl_signal_add(&keyboard_grab->events.destroy, + &relay->input_method_keyboard_grab_destroy); + relay->input_method_keyboard_grab_destroy.notify = + handle_im_keyboard_grab_destroy; +} + +static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void *data) { + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + input_method_keyboard_grab_destroy); + struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; + wlr_log(WLR_DEBUG,"im_keyboard_grab_destroy"); + wl_list_remove(&relay->input_method_keyboard_grab_destroy.link); + if (keyboard_grab->keyboard) { + // send modifier state to original client + wlr_seat_keyboard_notify_modifiers(keyboard_grab->input_method->seat, + &keyboard_grab->keyboard->modifiers); + } +} + + +static struct dwl_text_input *relay_get_focusable_text_input( + struct dwl_input_method_relay *relay) { + struct dwl_text_input *text_input = NULL; + wl_list_for_each(text_input, &relay->text_inputs, link) { + if (text_input->pending_focused_surface) { + return text_input; + } + } + return NULL; +} + +static struct dwl_text_input *relay_get_focused_text_input( + struct dwl_input_method_relay *relay) { + struct dwl_text_input *text_input = NULL; + wl_list_for_each(text_input, &relay->text_inputs, link) { + if (text_input->input->focused_surface) { + return text_input; + } + } + return NULL; +} + +static void handle_im_commit(struct wl_listener *listener, void *data) { + struct wlr_input_method_v2 *context; + + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + input_method_commit); + + struct dwl_text_input *text_input = relay_get_focused_text_input(relay); + if (!text_input) { + return; + } + + context = data; + assert(context == relay->input_method); + if (context->current.preedit.text) { + wlr_text_input_v3_send_preedit_string(text_input->input, + context->current.preedit.text, + context->current.preedit.cursor_begin, + context->current.preedit.cursor_end); + wlr_log(WLR_DEBUG,"preedit_text: %s", context->current.preedit.text); + } + if (context->current.commit_text) { + wlr_text_input_v3_send_commit_string(text_input->input, + context->current.commit_text); + wlr_log(WLR_DEBUG,"commit_text: %s", context->current.commit_text); + } + if (context->current.delete.before_length + || context->current.delete.after_length) { + wlr_text_input_v3_send_delete_surrounding_text(text_input->input, + context->current.delete.before_length, + context->current.delete.after_length); + } + wlr_text_input_v3_send_done(text_input->input); +} + +static void text_input_set_pending_focused_surface( + struct dwl_text_input *text_input, struct wlr_surface *surface) { + wl_list_remove(&text_input->pending_focused_surface_destroy.link); + text_input->pending_focused_surface = surface; + + if (surface) { + wl_signal_add(&surface->events.destroy, + &text_input->pending_focused_surface_destroy); + } else { + wl_list_init(&text_input->pending_focused_surface_destroy.link); + } +} + +static void handle_im_destroy(struct wl_listener *listener, void *data) { + struct dwl_text_input *text_input; + + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + input_method_destroy); + struct wlr_input_method_v2 *context = data; + wlr_log(WLR_INFO,"IM destroy"); + assert(context == relay->input_method); + relay->input_method = NULL; + + text_input = relay_get_focused_text_input(relay); + if (text_input) { + // keyboard focus is still there, so keep the surface at hand in case + // the input method returns + text_input_set_pending_focused_surface(text_input, + text_input->input->focused_surface); + wlr_text_input_v3_send_leave(text_input->input); + } +} + +static void relay_send_im_state(struct dwl_input_method_relay *relay, + struct wlr_text_input_v3 *input) { + struct wlr_input_method_v2 *input_method = relay->input_method; + if (!input_method) { + wlr_log(WLR_INFO, "Sending IM_DONE but im is gone"); + return; + } + // TODO: only send each of those if they were modified + wlr_input_method_v2_send_surrounding_text(input_method, + input->current.surrounding.text, input->current.surrounding.cursor, + input->current.surrounding.anchor); + wlr_input_method_v2_send_text_change_cause(input_method, + input->current.text_change_cause); + wlr_input_method_v2_send_content_type(input_method, + input->current.content_type.hint, input->current.content_type.purpose); + wlr_input_method_v2_send_done(input_method); + // TODO: pass intent, display popup size +} + +static void handle_text_input_enable(struct wl_listener *listener, void *data) { + struct dwl_text_input *text_input = wl_container_of(listener, text_input, + text_input_enable); + if (text_input->relay->input_method == NULL) { + wlr_log(WLR_INFO, "text_input_enable but input method is NULL"); + return; + } + + wlr_log(WLR_INFO,"text_input_enable"); +#ifdef XWAYLAND + wlr_input_method_v2_send_activate(text_input->relay->input_method); + wlr_log(WLR_INFO,"input_method activate for xwayland"); +#endif + + relay_send_im_state(text_input->relay, text_input->input); +} + +/* static void handle_text_input_commit(struct wl_listener *listener, */ +/* void *data) { */ +/* struct dwl_text_input *text_input = wl_container_of(listener, text_input, */ +/* text_input_commit); */ +/* if (!text_input->input->current_enabled) { */ +/* wlr_log(WLR_INFO, "text_input_commit but not enabled"); */ +/* return; */ +/* } */ +/* if (text_input->relay->input_method == NULL) { */ +/* wlr_log(WLR_INFO, "text_input_commit but input method is NULL"); */ +/* return; */ +/* } */ +/* wlr_log(WLR_DEBUG, "text_input_commit"); */ +/* relay_send_im_state(text_input->relay, text_input->input); */ +/* } */ + +static void relay_disable_text_input(struct dwl_input_method_relay *relay, + struct dwl_text_input *text_input) { + if (relay->input_method == NULL) { + wlr_log(WLR_INFO, "text_input_disable, but input method is NULL"); + return; + } + wlr_log(WLR_INFO,"text_input_disable"); + +#ifdef XWAYLAND + // https://gitee.com/guyuming76/dwl/commit/59328d6ecbbef1b1cd6e5ea8d90d78ccddd5c263 + wlr_input_method_v2_send_deactivate(relay->input_method); + wlr_log(WLR_INFO,"input_method deactivate for xwayland"); +#endif + //but if you keep the line above while remove the line below, input Chinese in geogebra(xwayland) won't work + relay_send_im_state(relay, text_input->input); +} + + +static void handle_text_input_destroy(struct wl_listener *listener, + void *data) { + struct dwl_text_input *text_input = wl_container_of(listener, text_input, + text_input_destroy); + + if (text_input->input->current_enabled) { + wlr_log(WLR_INFO,"text_input_destroy when still enabled"); + relay_disable_text_input(text_input->relay, text_input); + } + else { + wlr_log(WLR_INFO,"text_input_destroy"); + } + + text_input_set_pending_focused_surface(text_input, NULL); + //wl_list_remove(&text_input->text_input_commit.link); + wl_list_remove(&text_input->text_input_destroy.link); + //wl_list_remove(&text_input->text_input_disable.link); + wl_list_remove(&text_input->text_input_enable.link); + wl_list_remove(&text_input->link); + free(text_input); +} + +static void handle_pending_focused_surface_destroy(struct wl_listener *listener, + void *data) { + struct dwl_text_input *text_input = wl_container_of(listener, text_input, + pending_focused_surface_destroy); + struct wlr_surface *surface = data; + assert(text_input->pending_focused_surface == surface); + text_input->pending_focused_surface = NULL; + wl_list_remove(&text_input->pending_focused_surface_destroy.link); + wl_list_init(&text_input->pending_focused_surface_destroy.link); +} + +struct dwl_text_input *dwl_text_input_create( + struct dwl_input_method_relay *relay, + struct wlr_text_input_v3 *text_input) { + struct dwl_text_input *input; + input = calloc(1, sizeof(*input)); + if (!input) { + wlr_log(WLR_ERROR, "dwl_text_input_create calloc failed"); + return NULL; + } + wlr_log(WLR_INFO, "dwl_text_input_create"); + input->input = text_input; + input->relay = relay; + + wl_list_insert(&relay->text_inputs, &input->link); + + input->text_input_enable.notify = handle_text_input_enable; + wl_signal_add(&text_input->events.enable, &input->text_input_enable); + + //input->text_input_commit.notify = handle_text_input_commit; + //wl_signal_add(&text_input->events.commit, &input->text_input_commit); + + /* input->text_input_disable.notify = handle_text_input_disable; */ + /* wl_signal_add(&text_input->events.disable, &input->text_input_disable); */ + + input->text_input_destroy.notify = handle_text_input_destroy; + wl_signal_add(&text_input->events.destroy, &input->text_input_destroy); + + input->pending_focused_surface_destroy.notify = + handle_pending_focused_surface_destroy; + wl_list_init(&input->pending_focused_surface_destroy.link); + + return input; +} + +static void relay_handle_text_input(struct wl_listener *listener, + void *data) { + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + text_input_new); + struct wlr_text_input_v3 *wlr_text_input = data; + if (seat != wlr_text_input->seat) { + return; + } + + dwl_text_input_create(relay, wlr_text_input); +} + + +static LayerSurface* layer_surface_from_wlr_layer_surface_v1( + struct wlr_layer_surface_v1* layer_surface) { + return layer_surface->data; +} + + +static void get_parent_and_output_box(struct wlr_surface *focused_surface, + struct wlr_box *parent, struct wlr_box *output_box) { + struct wlr_output *output; + struct wlr_box output_box_tmp; + struct wlr_layer_surface_v1 *layer_surface; + + if ((layer_surface=wlr_layer_surface_v1_try_from_wlr_surface(focused_surface))) { + LayerSurface* layer = + layer_surface_from_wlr_layer_surface_v1(layer_surface); + output = layer->layer_surface->output; + wlr_output_layout_get_box(output_layout, output,&output_box_tmp); + *parent = layer->geom; + parent->x += output_box_tmp.x; + parent->y += output_box_tmp.y; + wlr_log(WLR_INFO,"get_parent_and_output_box layersurface output_box_tmp->x %d y %d",output_box_tmp.x, output_box_tmp.y); + wlr_log(WLR_INFO,"get_parent_and_output_box layersurface parent->x %d y %d",parent->x,parent->y); + } else { + //Client *client = client_from_wlr_surface(focused_surface); + Client *client = NULL; + LayerSurface *l = NULL; + int type = toplevel_from_wlr_surface(focused_surface, &client, &l); + + output = wlr_output_layout_output_at(output_layout, + client->geom.x, client->geom.y); + wlr_output_layout_get_box(output_layout, output,&output_box_tmp); + + parent->x = client->geom.x + client->bw; + parent->y = client->geom.y + client->bw; + parent->width = client->geom.width; + parent->height = client->geom.height; + wlr_log(WLR_INFO,"get_parent_and_output_box client output_box_tmp->x %d y %d",output_box_tmp.x, output_box_tmp.y); + wlr_log(WLR_INFO,"get_parent_and_output_box client client->geom.x %d y %d",client->geom.x,client->geom.y); + wlr_log(WLR_INFO,"get_parent_and_output_box client client->bw %d",client->bw); + wlr_log(WLR_INFO,"get_parent_and_output_box client parent->x %d y %d",parent->x,parent->y); + } + + //*output_box = output_box_tmp; + memcpy(output_box,&output_box_tmp,sizeof(struct wlr_box)); + wlr_log(WLR_INFO,"get_parent_and_output_box output_box x %d y %d width %d height %d",output_box->x,output_box->y,output_box->width,output_box->height); +} + +static void input_popup_update(struct dwl_input_popup *popup) { + struct wlr_surface* focused_surface; + struct wlr_box output_box, parent, cursor; + int x1, x2, y1, y2, x, y, available_right, available_left, available_down, + available_up, popup_width, popup_height; + bool cursor_rect, x1_in_bounds, y1_in_bounds, x2_in_bounds, y2_in_bounds; + + struct dwl_text_input *text_input = + relay_get_focused_text_input(popup->relay); + if (!text_input|| !text_input->input->focused_surface) { + return; + } + + //TODO: https://gitlab.freedesktop.org/wlroots/wlroots/-/commit/743da5c0ae723098fe772aadb93810f60e700ab9 + + if (!popup->popup_surface->surface->mapped) { + return; + } + + cursor_rect = text_input->input->current.features + & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; + + focused_surface = text_input->input->focused_surface; + cursor = text_input->input->current.cursor_rectangle; + + get_parent_and_output_box(focused_surface, &parent, &output_box); + + popup_width = popup->popup_surface->surface->current.width; + popup_height = popup->popup_surface->surface->current.height; + + if (!cursor_rect) { + cursor.x = 0; + cursor.y = 0; + cursor.width = parent.width; + cursor.height = parent.height; + wlr_log(WLR_INFO,"input_popup_update !cursor_rect"); + + popup->x=parent.x; + popup->y=parent.y; + popup->visible=true; + } + else { + wlr_log(WLR_INFO,"input_popup_update cursor x %d y %d width %d height %d",cursor.x,cursor.y,cursor.width,cursor.height); + + x1 = parent.x + cursor.x; + x2 = parent.x + cursor.x + cursor.width; + y1 = parent.y + cursor.y; + y2 = parent.y + cursor.y + cursor.height; + x = x1; + y = y2; + + wlr_log(WLR_INFO,"input_popup_update x1 %d x2 %d y1 %d y2 %d; x %d y %d",x1,x2,y1,y2,x,y); + available_right = output_box.x + output_box.width - x1; + available_left = x2 - output_box.x; + if (available_right < popup_width && available_left > available_right) { + x = x2 - popup_width; + wlr_log(WLR_INFO,"input_popup_update available_left %d popup_width %d available_right %d; x %d",available_left,popup_width,available_right,x); + } + + available_down = output_box.y + output_box.height - y2; + available_up = y1 - output_box.y; + if (available_down < popup_height && available_up > available_down) { + y = y1 - popup_height; + wlr_log(WLR_INFO,"input_popup_update available up %d popup_height %d available_down %d; y %d",available_up,popup_height,available_down,y); + } + + popup->x = x; + popup->y = y; + + // Hide popup if cursor position is completely out of bounds + x1_in_bounds = (cursor.x >= 0 && cursor.x < parent.width); + y1_in_bounds = (cursor.y >= 0 && cursor.y < parent.height); + x2_in_bounds = (cursor.x + cursor.width >= 0 + && cursor.x + cursor.width < parent.width); + y2_in_bounds = (cursor.y + cursor.height >= 0 + && cursor.y + cursor.height < parent.height); + popup->visible = + (x1_in_bounds && y1_in_bounds) || (x2_in_bounds && y2_in_bounds); + + struct wlr_box box = { + .x = x1 - x, + .y = y1 - y, + .width = cursor.width, + .height = cursor.height, + }; + wlr_input_popup_surface_v2_send_text_input_rectangle( + popup->popup_surface, &box); + wlr_log(WLR_INFO,"input_popup_update send_text_input_rect box.x %d box.y %d",box.x,box.y); + + } + + wlr_log(WLR_INFO,"input_popup_update x %d y %d visible %s",popup->x,popup->y,popup->visible?"true":"false"); + wlr_scene_node_set_position(&popup->scene->node, popup->x, popup->y); +} + +static void handle_im_popup_map(struct wl_listener *listener, void *data) { + struct dwl_input_popup *popup = + wl_container_of(listener, popup, popup_map); + + wlr_log(WLR_INFO, "IM_popup_map"); + + //popup->scene = &wlr_scene_tree_create(layers[LyrIMPopup])->node; + popup->scene = wlr_scene_tree_create(layers[LyrIMPopup]); + popup->scene_surface = wlr_scene_subsurface_tree_create(popup->scene,popup->popup_surface->surface); + //popup->scene_surface = &wlr_scene_subsurface_tree_create(popup->scene->parent,popup->popup_surface->surface)->node; + //popup->scene_surface->data = popup; + popup->scene_surface->node.data = popup; + + input_popup_update(popup); + + //wlr_scene_node_set_position(popup->scene, popup->x, popup->y); +} + +static void handle_im_popup_unmap(struct wl_listener *listener, void *data) { + struct dwl_input_popup *popup = + wl_container_of(listener, popup, popup_unmap); + //input_popup_update(popup); + + wlr_log(WLR_INFO,"im_popup_unmap"); + wlr_scene_node_destroy(&popup->scene->node); +} + +static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { + struct dwl_input_method_relay *relay; + struct dwl_input_popup *popup = + wl_container_of(listener, popup, popup_destroy); + + wlr_log(WLR_INFO,"im_popup_destroy"); + + wl_list_remove(&popup->popup_destroy.link); + wl_list_remove(&popup->popup_unmap.link); + wl_list_remove(&popup->popup_map.link); + + relay=popup->relay; + free(popup->relay->popup); + relay->popup=NULL; +} + + +static void handle_im_new_popup_surface(struct wl_listener *listener, void *data) { + struct dwl_text_input* text_input; + + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + input_method_new_popup_surface); + struct dwl_input_popup *popup = calloc(1, sizeof(*popup)); + + wlr_log(WLR_INFO, "IM_new_popup_surface"); + relay->popup = popup; + + popup->relay = relay; + popup->popup_surface = data; + popup->popup_surface->data = popup; + + + wl_signal_add(&popup->popup_surface->surface->events.map, &popup->popup_map); + popup->popup_map.notify = handle_im_popup_map; + + wl_signal_add(&popup->popup_surface->surface->events.unmap, &popup->popup_unmap); + popup->popup_unmap.notify = handle_im_popup_unmap; + + wl_signal_add( + &popup->popup_surface->events.destroy, &popup->popup_destroy); + popup->popup_destroy.notify = handle_im_popup_destroy; +} + + +static void relay_handle_input_method(struct wl_listener *listener, + void *data) { + struct dwl_text_input *text_input; + + struct dwl_input_method_relay *relay = wl_container_of(listener, relay, + input_method_new); + + struct wlr_input_method_v2 *input_method = data; + if (seat != input_method->seat) { + wlr_log(WLR_INFO,"input_method_new Seat does not match"); + return; + } + + if (relay->input_method != NULL) { + wlr_log(WLR_INFO, "input_method_new Attempted to connect second input method to a seat"); + wlr_input_method_v2_send_unavailable(input_method); + return; + } + + wlr_log(WLR_INFO,"input_method_new"); + + relay->input_method = input_method; + wl_signal_add(&relay->input_method->events.commit, + &relay->input_method_commit); + relay->input_method_commit.notify = handle_im_commit; + wl_signal_add(&relay->input_method->events.new_popup_surface, + &relay->input_method_new_popup_surface); + relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface; + wl_signal_add(&relay->input_method->events.grab_keyboard, + &relay->input_method_grab_keyboard); + relay->input_method_grab_keyboard.notify = handle_im_grab_keyboard; + wl_signal_add(&relay->input_method->events.destroy, + &relay->input_method_destroy); + relay->input_method_destroy.notify = handle_im_destroy; + + wlr_input_method_v2_send_activate(relay->input_method); + + text_input = relay_get_focusable_text_input(relay); + if (text_input) { + wlr_text_input_v3_send_enter(text_input->input, + text_input->pending_focused_surface); + text_input_set_pending_focused_surface(text_input, NULL); + } +} + +void dwl_input_method_relay_init(struct dwl_input_method_relay *relay) { + wl_list_init(&relay->text_inputs); + + relay->popup=NULL; + + relay->text_input_new.notify = relay_handle_text_input; + wl_signal_add(&text_input_manager->events.text_input, + &relay->text_input_new); + + relay->input_method_new.notify = relay_handle_input_method; + wl_signal_add(&input_method_manager->events.input_method, + &relay->input_method_new); +} + +void dwl_input_method_relay_set_focus(struct dwl_input_method_relay *relay, + struct wlr_surface *surface) { + struct dwl_text_input *text_input; + wl_list_for_each(text_input, &relay->text_inputs, link) { + if (text_input->pending_focused_surface) { + assert(text_input->input->focused_surface == NULL); + if (surface != text_input->pending_focused_surface) { + text_input_set_pending_focused_surface(text_input, NULL); + } + } else if (text_input->input->focused_surface) { + assert(text_input->pending_focused_surface == NULL); + if (surface != text_input->input->focused_surface) { + relay_disable_text_input(relay, text_input); + wlr_text_input_v3_send_leave(text_input->input); + wlr_log(WLR_INFO, "wlr_text_input_send_leave"); + } else { + wlr_log(WLR_INFO, "IM relay set_focus already focused"); + continue; + } + } + + if (surface + && wl_resource_get_client(text_input->input->resource) + == wl_resource_get_client(surface->resource)) { + if (relay->input_method) { + wlr_text_input_v3_send_enter(text_input->input, surface); + wlr_log(WLR_INFO, "wlr_text_input_send_enter"); + if (relay->popup) input_popup_update(relay->popup); + } else { + text_input_set_pending_focused_surface(text_input, surface); + } + } + } +} + +#endif diff --git a/LICENSE.dwm b/LICENSE.dwm new file mode 100644 index 0000000..507e4dc --- /dev/null +++ b/LICENSE.dwm @@ -0,0 +1,39 @@ +Portions of dwl based on dwm code are used under the following license: + +MIT/X Consortium License + +© 2006-2019 Anselm R Garbe +© 2006-2009 Jukka Salmi +© 2006-2007 Sander van Dijk +© 2007-2011 Peter Hartlich +© 2007-2009 Szabolcs Nagy +© 2007-2009 Christof Musik +© 2007-2009 Premysl Hruby +© 2007-2008 Enno Gottox Boland +© 2008 Martin Hurton +© 2008 Neale Pickett +© 2009 Mate Nagy +© 2010-2016 Hiltjo Posthuma +© 2010-2012 Connor Lane Smith +© 2011 Christoph Lohmann <20h@r-36.net> +© 2015-2016 Quentin Rameau +© 2015-2016 Eric Pruitt +© 2016-2017 Markus Teich + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/LICENSE.sway b/LICENSE.sway new file mode 100644 index 0000000..3e0cacc --- /dev/null +++ b/LICENSE.sway @@ -0,0 +1,19 @@ +Copyright (c) 2016-2017 Drew DeVault + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LICENSE.tinywl b/LICENSE.tinywl new file mode 100644 index 0000000..7023690 --- /dev/null +++ b/LICENSE.tinywl @@ -0,0 +1,127 @@ +dwl is originally based on TinyWL, which is used under the following license: + +This work is licensed under CC0, which effectively puts it in the public domain. + +--- + +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/LICENSE_1 b/LICENSE_1 new file mode 100644 index 0000000..658085a --- /dev/null +++ b/LICENSE_1 @@ -0,0 +1,692 @@ +dwl - dwm for Wayland + +Copyright © 2020 dwl team + +See also the files LICENSE.tinywl, LICENSE.dwm and LICENSE.sway. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +---- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md index 5f41800..72236f6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,63 @@ -# plumewm -A streamlined but feature-rich wayland window tiling manager + +https://github.com/user-attachments/assets/84eb21fd-1782-4edc-b504-50de35201abe + + + +# feature +- dwl ipc support +- maxmize fullscreen +- swap window by direction +- swith to next not empty-workspace/workspace +- move window to next not empty-workspace/workspace +- swith focus by direction +- wheel/button + mod key bind +- hycov like overview +- numlock state pre-set in config +- portal fix for obs(use hyprland-portal) +- master layout support new_on_top +- foreign-toplevel support(dunst,waybar wlr taskbar) +- acitve on focus/urgent support +- hide empty button in waybar +- support minimize window by click waybar +- support minimize window by keyboard +- sway scratchpad support +- window pin mode support +- text-input-v2 +- window move/open animaition + + +# install +# wlroots(0.17) +``` +git clone -b 0.17.4 https://gitlab.freedesktop.org/wlroots/wlroots.git +cd wlroots +meson build -Dprefix=/usr +sudo ninja -C build install + +git clone https://github.com/DreamMaoMao/mydwl.git +cd mydwl +meson build -Dprefix=/usr +sudo ninja -C build install + +sed -i s#/home/user#$HOME#g dwl.desktop +sudo cp dwl.desktop /usr/share/wayland-sessions/ +``` +# dependcy + +## tools +``` +yay -S wofi foot + +``` +# waybar (must use this waybar) +``` +git clone https://gitee.com/DreamMaoMao/Waybar.git +cd mywaybar +meson build -Dprefix=/usr +sudo ninja -C build install +``` + +# thanks for some refer repo +https://github.com/dqrk0jeste/owl - for window animation implementation code +https://github.com/djpohly/dwl - for base dwl code + diff --git a/autostart.sh b/autostart.sh new file mode 100755 index 0000000..ea54097 --- /dev/null +++ b/autostart.sh @@ -0,0 +1,15 @@ +#! /bin/bash + +set +e + +systemctl --user unmask xdg-desktop-portal-hyprland +systemctl --user mask xdg-desktop-portal-gnome + +dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=sway + +/usr/lib/xdg-desktop-portal-hyprland & + +wl-clip-persist --clipboard regular & +wl-paste --type text --watch cliphist store & + + diff --git a/client.h b/client.h new file mode 100644 index 0000000..800b867 --- /dev/null +++ b/client.h @@ -0,0 +1,408 @@ +/* + * Attempt to consolidate unavoidable suck into one file, away from dwl.c. This + * file is not meant to be pretty. We use a .h file with static inline + * functions instead of a separate .c module, or function pointers like sway, so + * that they will simply compile out if the chosen #defines leave them unused. + */ + +/* Leave these functions first; they're used in the others */ +static inline int +client_is_x11(Client *c) +{ +#ifdef XWAYLAND + return c->type == X11; +#endif + return 0; +} + +static inline struct wlr_surface * +client_surface(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->surface; +#endif + return c->surface.xdg->surface; +} + +static inline int +toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, LayerSurface **pl) +{ + struct wlr_xdg_surface *xdg_surface, *tmp_xdg_surface; + struct wlr_surface *root_surface; + struct wlr_layer_surface_v1 *layer_surface; + Client *c = NULL; + LayerSurface *l = NULL; + int type = -1; +#ifdef XWAYLAND + struct wlr_xwayland_surface *xsurface; +#endif + + if (!s) + return -1; + root_surface = wlr_surface_get_root_surface(s); + +#ifdef XWAYLAND + if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(root_surface))) { + c = xsurface->data; + type = c->type; + goto end; + } +#endif + + if ((layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(root_surface))) { + l = layer_surface->data; + type = LayerShell; + goto end; + } + + xdg_surface = wlr_xdg_surface_try_from_wlr_surface(root_surface); + while (xdg_surface) { + tmp_xdg_surface = NULL; + switch (xdg_surface->role) { + case WLR_XDG_SURFACE_ROLE_POPUP: + if (!xdg_surface->popup || !xdg_surface->popup->parent) + return -1; + + tmp_xdg_surface = wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent); + + if (!tmp_xdg_surface) + return toplevel_from_wlr_surface(xdg_surface->popup->parent, pc, pl); + + xdg_surface = tmp_xdg_surface; + break; + case WLR_XDG_SURFACE_ROLE_TOPLEVEL: + c = xdg_surface->data; + type = c->type; + goto end; + case WLR_XDG_SURFACE_ROLE_NONE: + return -1; + } + } + +end: + if (pl) + *pl = l; + if (pc) + *pc = c; + return type; +} + +/* The others */ +static inline void +client_activate_surface(struct wlr_surface *s, int activated) +{ + struct wlr_xdg_toplevel *toplevel; +#ifdef XWAYLAND + struct wlr_xwayland_surface *xsurface; + if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) { + wlr_xwayland_surface_activate(xsurface, activated); + return; + } +#endif + if ((toplevel = wlr_xdg_toplevel_try_from_wlr_surface(s))) + wlr_xdg_toplevel_set_activated(toplevel, activated); +} + +static inline uint32_t +client_set_bounds(Client *c, int32_t width, int32_t height) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return 0; +#endif + if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >= + XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION && width >= 0 && height >= 0 + && (c->bounds.width != width || c->bounds.height != height)) { + c->bounds.width = width; + c->bounds.height = height; + return wlr_xdg_toplevel_set_bounds(c->surface.xdg->toplevel, width, height); + } + return 0; +} + +static inline const char * +client_get_appid(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->class; +#endif + return c->surface.xdg->toplevel->app_id; +} + +static inline void +client_get_clip(Client *c, struct wlr_box *clip) +{ + struct wlr_box xdg_geom = {0}; + *clip = (struct wlr_box){ + .x = 0, + .y = 0, + .width = c->geom.width - c->bw, + .height = c->geom.height - c->bw, + }; + +#ifdef XWAYLAND + if (client_is_x11(c)) + return; +#endif + + wlr_xdg_surface_get_geometry(c->surface.xdg, &xdg_geom); + clip->x = xdg_geom.x; + clip->y = xdg_geom.y; +} + +static inline void +client_get_geometry(Client *c, struct wlr_box *geom) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) { + geom->x = c->surface.xwayland->x; + geom->y = c->surface.xwayland->y; + geom->width = c->surface.xwayland->width; + geom->height = c->surface.xwayland->height; + return; + } +#endif + wlr_xdg_surface_get_geometry(c->surface.xdg, geom); +} + +static inline Client * +client_get_parent(Client *c) +{ + Client *p = NULL; +#ifdef XWAYLAND + if (client_is_x11(c)) { + if (c->surface.xwayland->parent) + toplevel_from_wlr_surface(c->surface.xwayland->parent->surface, &p, NULL); + return p; + } +#endif + if (c->surface.xdg->toplevel->parent) + toplevel_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface, &p, NULL); + return p; +} + +static inline int +client_has_children(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return !wl_list_empty(&c->surface.xwayland->children); +#endif + /* surface.xdg->link is never empty because it always contains at least the + * surface itself. */ + return wl_list_length(&c->surface.xdg->link) > 1; +} + +static inline const char * +client_get_title(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->title; +#endif + return c->surface.xdg->toplevel->title; +} + +static inline int +client_is_float_type(Client *c) +{ + struct wlr_xdg_toplevel *toplevel; + struct wlr_xdg_toplevel_state state; + +#ifdef XWAYLAND + if (client_is_x11(c)) { + struct wlr_xwayland_surface *surface = c->surface.xwayland; + xcb_size_hints_t *size_hints = surface->size_hints; + size_t i; + if (surface->modal) + return 1; + + for (i = 0; i < surface->window_type_len; i++) + if (surface->window_type[i] == netatom[NetWMWindowTypeDialog] + || surface->window_type[i] == netatom[NetWMWindowTypeSplash] + || surface->window_type[i] == netatom[NetWMWindowTypeToolbar] + || surface->window_type[i] == netatom[NetWMWindowTypeUtility]) + return 1; + + return size_hints && size_hints->min_width > 0 && size_hints->min_height > 0 + && (size_hints->max_width == size_hints->min_width + || size_hints->max_height == size_hints->min_height); + } +#endif + + toplevel = c->surface.xdg->toplevel; + state = toplevel->current; + return toplevel->parent || (state.min_width != 0 && state.min_height != 0 + && (state.min_width == state.max_width + || state.min_height == state.max_height)); +} + +static inline int +client_is_rendered_on_mon(Client *c, Monitor *m) +{ + /* This is needed for when you don't want to check formal assignment, + * but rather actual displaying of the pixels. + * Usually VISIBLEON suffices and is also faster. */ + struct wlr_surface_output *s; + int unused_lx, unused_ly; + if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly)) + return 0; + wl_list_for_each(s, &client_surface(c)->current_outputs, link) + if (s->output == m->wlr_output) + return 1; + return 0; +} + +static inline int +client_is_stopped(Client *c) +{ + int pid; + siginfo_t in = {0}; +#ifdef XWAYLAND + if (client_is_x11(c)) + return 0; +#endif + + wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); + if (waitid(P_PID, pid, &in, WNOHANG|WCONTINUED|WSTOPPED|WNOWAIT) < 0) { + /* This process is not our child process, while is very unluckely that + * it is stopped, in order to do not skip frames assume that it is. */ + if (errno == ECHILD) + return 1; + } else if (in.si_pid) { + if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED) + return 1; + if (in.si_code == CLD_CONTINUED) + return 0; + } + + return 0; +} + +static inline int +client_is_unmanaged(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->override_redirect; +#endif + return 0; +} + +static inline void +client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb) +{ + if (kb) + wlr_seat_keyboard_notify_enter(seat, s, kb->keycodes, + kb->num_keycodes, &kb->modifiers); + else + wlr_seat_keyboard_notify_enter(seat, s, NULL, 0, NULL); +} + +static inline void +client_restack_surface(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + wlr_xwayland_surface_restack(c->surface.xwayland, NULL, + XCB_STACK_MODE_ABOVE); +#endif + return; +} + +static inline void +client_send_close(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) { + wlr_xwayland_surface_close(c->surface.xwayland); + return; + } +#endif + wlr_xdg_toplevel_send_close(c->surface.xdg->toplevel); +} + +static inline void +client_set_border_color(Client *c, const float color[static 4]) +{ + int i; + for (i = 0; i < 4; i++) + wlr_scene_rect_set_color(c->border[i], color); +} + +static inline void +client_set_fullscreen(Client *c, int fullscreen) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) { + wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen); + return; + } +#endif + wlr_xdg_toplevel_set_fullscreen(c->surface.xdg->toplevel, fullscreen); +} + +static inline uint32_t +client_set_size(Client *c, uint32_t width, uint32_t height) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) { + wlr_xwayland_surface_configure(c->surface.xwayland, + c->geom.x, c->geom.y, width, height); + return 0; + } +#endif + if ((int32_t)width == c->surface.xdg->toplevel->current.width + && (int32_t)height == c->surface.xdg->toplevel->current.height) + return 0; + return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, (int32_t)height); +} + +static inline void +client_set_tiled(Client *c, uint32_t edges) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return; +#endif + if (wl_resource_get_version(c->surface.xdg->toplevel->resource) + >= XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) { + wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges); + } else { + wlr_xdg_toplevel_set_maximized(c->surface.xdg->toplevel, edges != WLR_EDGE_NONE); + } +} + +static inline void +client_set_suspended(Client *c, int suspended) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return; +#endif + + wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended); +} + +static inline int +client_wants_focus(Client *c) +{ +#ifdef XWAYLAND + return client_is_unmanaged(c) + && wlr_xwayland_or_surface_wants_focus(c->surface.xwayland) + && wlr_xwayland_icccm_input_model(c->surface.xwayland) != WLR_ICCCM_INPUT_MODEL_NONE; +#endif + return 0; +} + +static inline int +client_wants_fullscreen(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->fullscreen; +#endif + return c->surface.xdg->toplevel->requested.fullscreen; +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..0c9e9c9 --- /dev/null +++ b/config.h @@ -0,0 +1,265 @@ +/* speedie's dwl config */ + +#define COLOR(hex) { ((hex >> 24) & 0xFF) / 255.0f, \ + ((hex >> 16) & 0xFF) / 255.0f, \ + ((hex >> 8) & 0xFF) / 255.0f, \ + (hex & 0xFF) / 255.0f } + +/* animation */ +static const bool animations = true; // Enable animations +static const float zoom_initial_ratio = 0.5; // Initial window ratio for animations +static const uint32_t animation_duration = 300; // Animation speed +// static const double animation_curve[4] = {0.05,0.9,0.1,1.05}; // Animation curve +static const double animation_curve[4] = {0.46,1.0,0.29,0.99}; // Animation curve + +/* appearance */ +static const unsigned int axis_bind_apply_timeout = 100; // Timeout for wheel binding actions +static const unsigned int focus_on_activate = 1; // Automatically focus on window activation request +static const unsigned int new_is_master = 1; // New windows are inserted at the head +/* logging */ +static int log_level = WLR_ERROR; +static const unsigned int numlockon = 1; // Enable numlock +static const unsigned int hotarea_size = 10; // Hot area size, 10x10 +static const unsigned int enable_hotarea = 1; // Enable mouse hot area +static int smartgaps = 0; /* 1 means no outer gap when there is only one window */ +static int sloppyfocus = 1; /* Focus follows mouse */ +static unsigned int gappih = 5; /* Horizontal inner gap between windows */ +static unsigned int gappiv = 5; /* Vertical inner gap between windows */ +static unsigned int gappoh = 10; /* Horizontal outer gap between windows and screen edge */ +static unsigned int gappov = 10; /* Vertical outer gap between windows and screen edge */ +static int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if the surface isn't visible */ +static unsigned int borderpx = 5; /* Border pixel of windows */ +static const float rootcolor[] = COLOR(0x323232ff); +static const float bordercolor[] = COLOR(0x444444ff); +static const float focuscolor[] = COLOR(0xad741fff); +static const float fakefullscreencolor[] = COLOR(0x89aa61ff); +static const float urgentcolor[] = COLOR(0xad401fff); +static const float scratchpadcolor[] = COLOR(0x516c93ff); +static const float globalcolor[] = COLOR(0xb153a7ff); +// static const char *cursor_theme = "Bibata-Modern-Ice"; + +static const int overviewgappi = 5; /* Gap between windows and edges in overview mode */ +static const int overviewgappo = 30; /* Gap between windows in overview mode */ + +/* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */ +static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; + +static int warpcursor = 1; /* Warp cursor to focused client */ + +/* Autostart */ +static const char *const autostart[] = { + "/bin/sh", + "-c", + "$DWL/autostart.sh", + NULL, + NULL, +}; + +/* tagging + * expand the array to add more tags + */ +static const char *tags[] = { + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", +}; + +static const Rule rules[] = { + /* app_id title tags mask isfloating isfullscreen isnoclip isnoborder monitor width height */ + /* examples: + { "Gimp", NULL, 0, 1, -1,800,600 }, + */ + { "Google-chrome", NULL, 1 << 3, 0, 0, 0, 0,-1, 0,0}, + { NULL, "Image Viewer", 0, 1, 0, 0, 0,-1, 0,0}, + { NULL, "Image View", 0, 1, 0, 0, 0,-1, 0,0}, + { NULL, "File Selector", 0, 1, 0, 0, 0,-1, 1200,800}, + { NULL, "Open File", 0, 1, 0, 0, 0,-1, 1200,800}, + { "polkit-gnome-authentication-agent-1", NULL, 0, 1, 0, 1, 1,-1, 930,230}, + { "blueman-manager", NULL, 0, 1, 0, 0, 0,-1, 700,600}, + { "Gnome-system-monitor", NULL, 0, 0, 0, 0, 0,-1, 700,600}, + { "obs", NULL, 1<<5, 0, 0, 0, 0,-1, 700,600}, + { "flameshot", NULL, 0, 0, 1, 0, 0,-1, 0,0}, + { NULL, "rofi - Networks", 0, 1, 0, 1, 1,-1, 0,0}, + { "Rofi", NULL, 0, 1, 0, 1, 1,-1, 0,0}, + { "wofi", NULL, 0, 1, 0, 0, 1,-1, 0,0}, + +}; + +/* layout(s) */ +static const Layout overviewlayout = { "󰃇", overview }; + +static const Layout layouts[] = { // At least two layouts, cannot delete less than two + /* symbol arrange function */ + { "󱞬", tile }, // Stack layout + { "﩯", grid }, // Grid layout + +}; + +/* monitors */ +static const MonitorRule monrules[] = { + /* name mfact nmaster scale layout rotate/reflect x y*/ + /* example of a HiDPI laptop monitor: + { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL }, + */ + /* defaults */ + // { "eDP-1", 0.5, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL }, + { "eDP-1", 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0,0}, + { "HDMI-A-1", 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 1920,0}, +}; + +/* keyboard */ +static const struct xkb_rule_names xkb_rules = { + /* can specify fields: rules, model, layout, variant, options */ + /* example: + .options = "ctrl:nocaps", + */ + .options = NULL, +}; + +static int repeat_rate = 25; +static int repeat_delay = 600; + +/* Trackpad */ +static int tap_to_click = 1; +static int tap_and_drag = 1; +static int drag_lock = 1; +static int natural_scrolling = 0; +static int disable_while_typing = 1; +static int left_handed = 0; +static int middle_button_emulation = 0; +/* You can choose between: +LIBINPUT_CONFIG_SCROLL_NO_SCROLL +LIBINPUT_CONFIG_SCROLL_2FG +LIBINPUT_CONFIG_SCROLL_EDGE +LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN +*/ +static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; + +/* You can choose between: +LIBINPUT_CONFIG_CLICK_METHOD_NONE +LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS +LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER +*/ +static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; + +/* You can choose between: +LIBINPUT_CONFIG_SEND_EVENTS_ENABLED +LIBINPUT_CONFIG_SEND_EVENTS_DISABLED +LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE +*/ +static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; + +/* You can choose between: +LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT +LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE +*/ +static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; +static const double accel_speed = 0.0; +/* You can choose between: +LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle +LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right +*/ +static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; + +/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */ +#define MODKEY WLR_MODIFIER_ALT + +#define TAGKEYS(KEY,SKEY,TAG) \ + { WLR_MODIFIER_CTRL, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \ + { WLR_MODIFIER_ALT, KEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} } + +/* helper for spawning shell commands in the pre dwm-5.0 fashion */ +#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + +/* commands */ +// static const char *termcmd[] = { "foot", NULL }; +// static const char *menucmd[] = { "wofi --conf $DWL/wofi/config_menu", NULL }; + +static const Key keys[] = { + /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */ + /* modifier key function argument */ + { MODKEY, XKB_KEY_space, spawn, SHCMD("wofi") }, + { MODKEY, XKB_KEY_Return, spawn, SHCMD("foot") }, + + + { WLR_MODIFIER_LOGO, XKB_KEY_Tab, focusstack, {.i = +1} }, + + { WLR_MODIFIER_ALT, XKB_KEY_Left, focusdir, {.i = LEFT } }, /* alt left | Switch focus window within the current tag */ + { WLR_MODIFIER_ALT, XKB_KEY_Right, focusdir, {.i = RIGHT } }, /* alt right | Switch focus window within the current tag */ + { WLR_MODIFIER_ALT, XKB_KEY_Up, focusdir, {.i = UP } }, /* alt up | Switch focus window within the current tag */ + { WLR_MODIFIER_ALT, XKB_KEY_Down, focusdir, {.i = DOWN } }, + + + { WLR_MODIFIER_LOGO, XKB_KEY_e, incnmaster, {.i = +1} }, + { WLR_MODIFIER_LOGO, XKB_KEY_t, incnmaster, {.i = -1} }, + { WLR_MODIFIER_ALT|WLR_MODIFIER_CTRL, XKB_KEY_Left, setmfact, {.f = -0.01} }, + { WLR_MODIFIER_ALT|WLR_MODIFIER_CTRL, XKB_KEY_Right, setmfact, {.f = +0.01} }, + { MODKEY, XKB_KEY_s, zoom, {0} }, + { WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_Up, exchange_client, {.i = UP } }, /* super shift up | 2D exchange window (tiling only) */ + { WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_Down, exchange_client, {.i = DOWN } }, /* super shift down | 2D exchange window (tiling only) */ + { WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_Left, exchange_client, {.i = LEFT} }, /* super shift left | 2D exchange window (tiling only) */ + { WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_Right, exchange_client, {.i = RIGHT } }, /* super shift right | 2D exchange window (tiling only) */ + { WLR_MODIFIER_LOGO, XKB_KEY_g, toggleglobal, {0} }, + { MODKEY, XKB_KEY_Tab, toggleoverview, {0} }, + { WLR_MODIFIER_LOGO, XKB_KEY_Left, viewtoleft, {0} }, + { WLR_MODIFIER_CTRL, XKB_KEY_Left, viewtoleft_have_client, {0} }, + { WLR_MODIFIER_LOGO, XKB_KEY_Right, viewtoright, {0} }, + { WLR_MODIFIER_CTRL, XKB_KEY_Right, viewtoright_have_client, {0} }, + { WLR_MODIFIER_CTRL|WLR_MODIFIER_LOGO, XKB_KEY_Left, tagtoleft, {0} }, /* ctrl alt left | Move current window to the left tag */ + { WLR_MODIFIER_CTRL|WLR_MODIFIER_LOGO, XKB_KEY_Right, tagtoright, {0} }, + { MODKEY, XKB_KEY_q, killclient, {0} }, + { WLR_MODIFIER_CTRL|WLR_MODIFIER_LOGO, XKB_KEY_t, setlayout, {.v = &layouts[0]} }, + { WLR_MODIFIER_CTRL|WLR_MODIFIER_LOGO, XKB_KEY_g, setlayout, {.v = &layouts[1]} }, + { WLR_MODIFIER_LOGO, XKB_KEY_n, switch_layout, {0} }, + { WLR_MODIFIER_ALT, XKB_KEY_backslash, togglefloating, {0} }, + { MODKEY, XKB_KEY_a, togglefakefullscreen, {0} }, + { MODKEY, XKB_KEY_f, togglefullscreen, {0} }, + { WLR_MODIFIER_LOGO, XKB_KEY_i, minized, {0} }, // Minimize, move to scratchpad + { WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_I, restore_minized, {0} }, + { WLR_MODIFIER_ALT, XKB_KEY_z, toggle_scratchpad, {0} }, // Cycle through scratchpad + { WLR_MODIFIER_LOGO, XKB_KEY_bracketleft, focusmon, {.i = WLR_DIRECTION_LEFT} }, // super + [ + { WLR_MODIFIER_LOGO, XKB_KEY_bracketright, focusmon, {.i = WLR_DIRECTION_RIGHT} }, // suepr + ] + { WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_bracketleft, tagmon, {.i = WLR_DIRECTION_LEFT} }, + { WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_bracketright, tagmon, {.i = WLR_DIRECTION_RIGHT} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_X, incgaps, {.i = +1 } }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Z, incgaps, {.i = -1 } }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_R, togglegaps, {0} }, + TAGKEYS( XKB_KEY_KP_1, XKB_KEY_exclam, 0), + TAGKEYS( XKB_KEY_KP_2, XKB_KEY_at, 1), + TAGKEYS( XKB_KEY_KP_3, XKB_KEY_numbersign, 2), + TAGKEYS( XKB_KEY_KP_4, XKB_KEY_dollar, 3), + TAGKEYS( XKB_KEY_KP_5, XKB_KEY_percent, 4), + TAGKEYS( XKB_KEY_KP_6, XKB_KEY_asciicircum, 5), + TAGKEYS( XKB_KEY_KP_7, XKB_KEY_ampersand, 6), + TAGKEYS( XKB_KEY_KP_8, XKB_KEY_asterisk, 7), + TAGKEYS( XKB_KEY_KP_9, XKB_KEY_parenleft, 8), + //{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} }, + + /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */ + { WLR_MODIFIER_LOGO,XKB_KEY_m, quit, {0} }, +#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} } + CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6), + CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12), +}; + +static const Button buttons[] = { + { WLR_MODIFIER_LOGO, BTN_LEFT, moveresize, {.ui = CurMove } }, + { 0, BTN_MIDDLE, togglefakefullscreen, {0} }, // Middle button triggers fake fullscreen + { WLR_MODIFIER_LOGO, BTN_RIGHT, moveresize, {.ui = CurResize } }, + { WLR_MODIFIER_ALT|WLR_MODIFIER_CTRL, BTN_LEFT, spawn, SHCMD("bash ~/tool/shotTranslate.sh shot")}, + { 0, BTN_LEFT, toggleoverview, {0} }, + { 0, BTN_RIGHT, killclient, {0} }, +}; + +static const Axis axes[] = { + { WLR_MODIFIER_LOGO, AxisUp, viewtoleft_have_client, {0} }, // Middle button + super to switch workspace forward + { WLR_MODIFIER_LOGO, AxisDown, viewtoright_have_client, {0} }, // Middle button + super to switch workspace backward +}; \ No newline at end of file diff --git a/cursor-shape-v1-protocol.h b/cursor-shape-v1-protocol.h new file mode 100644 index 0000000..83e18af --- /dev/null +++ b/cursor-shape-v1-protocol.h @@ -0,0 +1,367 @@ +/* Generated by wayland-scanner 1.22.0 */ + +#ifndef CURSOR_SHAPE_V1_SERVER_PROTOCOL_H +#define CURSOR_SHAPE_V1_SERVER_PROTOCOL_H + +#include +#include +#include "wayland-server.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct wl_client; +struct wl_resource; + +/** + * @page page_cursor_shape_v1 The cursor_shape_v1 protocol + * @section page_ifaces_cursor_shape_v1 Interfaces + * - @subpage page_iface_wp_cursor_shape_manager_v1 - cursor shape manager + * - @subpage page_iface_wp_cursor_shape_device_v1 - cursor shape for a device + * @section page_copyright_cursor_shape_v1 Copyright + *
+ *
+ * Copyright 2018 The Chromium Authors
+ * Copyright 2023 Simon Ser
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ */ +struct wl_pointer; +struct wp_cursor_shape_device_v1; +struct wp_cursor_shape_manager_v1; +struct zwp_tablet_tool_v2; + +#ifndef WP_CURSOR_SHAPE_MANAGER_V1_INTERFACE +#define WP_CURSOR_SHAPE_MANAGER_V1_INTERFACE +/** + * @page page_iface_wp_cursor_shape_manager_v1 wp_cursor_shape_manager_v1 + * @section page_iface_wp_cursor_shape_manager_v1_desc Description + * + * This global offers an alternative, optional way to set cursor images. This + * new way uses enumerated cursors instead of a wl_surface like + * wl_pointer.set_cursor does. + * + * Warning! The protocol described in this file is currently in the testing + * phase. Backward compatible changes may be added together with the + * corresponding interface version bump. Backward incompatible changes can + * only be done by creating a new major version of the extension. + * @section page_iface_wp_cursor_shape_manager_v1_api API + * See @ref iface_wp_cursor_shape_manager_v1. + */ +/** + * @defgroup iface_wp_cursor_shape_manager_v1 The wp_cursor_shape_manager_v1 interface + * + * This global offers an alternative, optional way to set cursor images. This + * new way uses enumerated cursors instead of a wl_surface like + * wl_pointer.set_cursor does. + * + * Warning! The protocol described in this file is currently in the testing + * phase. Backward compatible changes may be added together with the + * corresponding interface version bump. Backward incompatible changes can + * only be done by creating a new major version of the extension. + */ +extern const struct wl_interface wp_cursor_shape_manager_v1_interface; +#endif +#ifndef WP_CURSOR_SHAPE_DEVICE_V1_INTERFACE +#define WP_CURSOR_SHAPE_DEVICE_V1_INTERFACE +/** + * @page page_iface_wp_cursor_shape_device_v1 wp_cursor_shape_device_v1 + * @section page_iface_wp_cursor_shape_device_v1_desc Description + * + * This interface advertises the list of supported cursor shapes for a + * device, and allows clients to set the cursor shape. + * @section page_iface_wp_cursor_shape_device_v1_api API + * See @ref iface_wp_cursor_shape_device_v1. + */ +/** + * @defgroup iface_wp_cursor_shape_device_v1 The wp_cursor_shape_device_v1 interface + * + * This interface advertises the list of supported cursor shapes for a + * device, and allows clients to set the cursor shape. + */ +extern const struct wl_interface wp_cursor_shape_device_v1_interface; +#endif + +/** + * @ingroup iface_wp_cursor_shape_manager_v1 + * @struct wp_cursor_shape_manager_v1_interface + */ +struct wp_cursor_shape_manager_v1_interface { + /** + * destroy the manager + * + * Destroy the cursor shape manager. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * manage the cursor shape of a pointer device + * + * Obtain a wp_cursor_shape_device_v1 for a wl_pointer object. + */ + void (*get_pointer)(struct wl_client *client, + struct wl_resource *resource, + uint32_t cursor_shape_device, + struct wl_resource *pointer); + /** + * manage the cursor shape of a tablet tool device + * + * Obtain a wp_cursor_shape_device_v1 for a zwp_tablet_tool_v2 + * object. + */ + void (*get_tablet_tool_v2)(struct wl_client *client, + struct wl_resource *resource, + uint32_t cursor_shape_device, + struct wl_resource *tablet_tool); +}; + + +/** + * @ingroup iface_wp_cursor_shape_manager_v1 + */ +#define WP_CURSOR_SHAPE_MANAGER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_wp_cursor_shape_manager_v1 + */ +#define WP_CURSOR_SHAPE_MANAGER_V1_GET_POINTER_SINCE_VERSION 1 +/** + * @ingroup iface_wp_cursor_shape_manager_v1 + */ +#define WP_CURSOR_SHAPE_MANAGER_V1_GET_TABLET_TOOL_V2_SINCE_VERSION 1 + +#ifndef WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ENUM +#define WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ENUM +/** + * @ingroup iface_wp_cursor_shape_device_v1 + * cursor shapes + * + * This enum describes cursor shapes. + * + * The names are taken from the CSS W3C specification: + * https://w3c.github.io/csswg-drafts/css-ui/#cursor + */ +enum wp_cursor_shape_device_v1_shape { + /** + * default cursor + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT = 1, + /** + * a context menu is available for the object under the cursor + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CONTEXT_MENU = 2, + /** + * help is available for the object under the cursor + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_HELP = 3, + /** + * pointer that indicates a link or another interactive element + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER = 4, + /** + * progress indicator + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_PROGRESS = 5, + /** + * program is busy, user should wait + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_WAIT = 6, + /** + * a cell or set of cells may be selected + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CELL = 7, + /** + * simple crosshair + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR = 8, + /** + * text may be selected + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT = 9, + /** + * vertical text may be selected + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_VERTICAL_TEXT = 10, + /** + * drag-and-drop: alias of/shortcut to something is to be created + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALIAS = 11, + /** + * drag-and-drop: something is to be copied + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COPY = 12, + /** + * drag-and-drop: something is to be moved + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE = 13, + /** + * drag-and-drop: the dragged item cannot be dropped at the current cursor location + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NO_DROP = 14, + /** + * drag-and-drop: the requested action will not be carried out + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NOT_ALLOWED = 15, + /** + * drag-and-drop: something can be grabbed + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRAB = 16, + /** + * drag-and-drop: something is being grabbed + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRABBING = 17, + /** + * resizing: the east border is to be moved + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_E_RESIZE = 18, + /** + * resizing: the north border is to be moved + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_N_RESIZE = 19, + /** + * resizing: the north-east corner is to be moved + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NE_RESIZE = 20, + /** + * resizing: the north-west corner is to be moved + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NW_RESIZE = 21, + /** + * resizing: the south border is to be moved + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_S_RESIZE = 22, + /** + * resizing: the south-east corner is to be moved + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SE_RESIZE = 23, + /** + * resizing: the south-west corner is to be moved + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SW_RESIZE = 24, + /** + * resizing: the west border is to be moved + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_W_RESIZE = 25, + /** + * resizing: the east and west borders are to be moved + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE = 26, + /** + * resizing: the north and south borders are to be moved + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE = 27, + /** + * resizing: the north-east and south-west corners are to be moved + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE = 28, + /** + * resizing: the north-west and south-east corners are to be moved + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE = 29, + /** + * resizing: that the item/column can be resized horizontally + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COL_RESIZE = 30, + /** + * resizing: that the item/row can be resized vertically + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ROW_RESIZE = 31, + /** + * something can be scrolled in any direction + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_SCROLL = 32, + /** + * something can be zoomed in + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_IN = 33, + /** + * something can be zoomed out + */ + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_OUT = 34, +}; +#endif /* WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ENUM */ + +#ifndef WP_CURSOR_SHAPE_DEVICE_V1_ERROR_ENUM +#define WP_CURSOR_SHAPE_DEVICE_V1_ERROR_ENUM +enum wp_cursor_shape_device_v1_error { + /** + * the specified shape value is invalid + */ + WP_CURSOR_SHAPE_DEVICE_V1_ERROR_INVALID_SHAPE = 1, +}; +#endif /* WP_CURSOR_SHAPE_DEVICE_V1_ERROR_ENUM */ + +/** + * @ingroup iface_wp_cursor_shape_device_v1 + * @struct wp_cursor_shape_device_v1_interface + */ +struct wp_cursor_shape_device_v1_interface { + /** + * destroy the cursor shape device + * + * Destroy the cursor shape device. + * + * The device cursor shape remains unchanged. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * set device cursor to the shape + * + * Sets the device cursor to the specified shape. The compositor + * will change the cursor image based on the specified shape. + * + * The cursor actually changes only if the input device focus is + * one of the requesting client's surfaces. If any, the previous + * cursor image (surface or shape) is replaced. + * + * The "shape" argument must be a valid enum entry, otherwise the + * invalid_shape protocol error is raised. + * + * This is similar to the wl_pointer.set_cursor and + * zwp_tablet_tool_v2.set_cursor requests, but this request accepts + * a shape instead of contents in the form of a surface. Clients + * can mix set_cursor and set_shape requests. + * + * The serial parameter must match the latest wl_pointer.enter or + * zwp_tablet_tool_v2.proximity_in serial number sent to the + * client. Otherwise the request will be ignored. + * @param serial serial number of the enter event + */ + void (*set_shape)(struct wl_client *client, + struct wl_resource *resource, + uint32_t serial, + uint32_t shape); +}; + + +/** + * @ingroup iface_wp_cursor_shape_device_v1 + */ +#define WP_CURSOR_SHAPE_DEVICE_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_wp_cursor_shape_device_v1 + */ +#define WP_CURSOR_SHAPE_DEVICE_V1_SET_SHAPE_SINCE_VERSION 1 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dwl.1 b/dwl.1 new file mode 100644 index 0000000..cae1036 --- /dev/null +++ b/dwl.1 @@ -0,0 +1,151 @@ +.Dd January 8, 2021 +.Dt DWL 1 +.Os +.Sh NAME +.Nm dwl +.Nd dwm for Wayland +.Sh SYNOPSIS +.Nm +.Op Fl v +.Op Fl s Ar startup command +.Sh DESCRIPTION +.Nm +is a Wayland compositor based on wlroots. +It is intended to fill the same space in the Wayland world that +.Nm dwm +does for X11. +.Pp +When given the +.Fl v +option, +.Nm +writes its name and version to standard error and exits unsuccessfully. +.Pp +When given the +.Fl s +option, +.Nm +starts a shell process running +.Ar command +when starting. +When stopping, it sends +.Dv SIGTERM +to the child process and waits for it to exit. +.Pp +Users are encouraged to customize +.Nm +by editing the sources, in particular +.Pa config.h . +The default key bindings are as follows: +.Bl -tag -width 20n -offset indent -compact +.It Mod-[1-9] +Show only all windows with a tag. +.It Mod-Ctrl-[1-9] +Show all windows with a tag. +.It Mod-Shift-[1-9] +Move window to a single tag. +.It Mod-Ctrl-Shift-[1-9] +Toggle tag for window. +.It Mod-p +Spawn +.Nm bemenu-run . +.It Mod-Shift-Return +Spawn +.Nm foot . +.It Mod-[jk] +Move focus down/up the stack. +.It Mod-[id] +Increase/decrease number of windows in master area. +.It Mod-[hl] +Decrease/increase master area. +.It Mod-Return +Move window on top of stack or switch top of stack with second window. +.It Mod-Tab +Show only all windows with previous tag. +.It Mod-Shift-c +Close window. +.It Mod-t +Switch to tabbed layout. +.It Mod-f +Switch to floating layout. +.It Mod-m +Switch to monocle layout. +.It Mod-Space +Switch to previous layout. +.It Mod-Shift-Space +Toggle floating state of window. +.It Mod-e +Toggle fullscreen state of window. +.It Mod-0 +Show all windows. +.It Mod-Shift-0 +Set all tags for window. +.It Mod-, +Move focus to previous monitor. +.It Mod-. +Move focus to next monitor. +.It Mod-Shift-, +Move window to previous monitor. +.It Mod-Shift-. +Move window to next monitor. +.It Mod-Shift-q +Quit +.Nm . +.El +These might differ depending on your keyboard layout. +.Sh ENVIRONMENT +These environment variables are used by +.Nm : +.Bl -tag -width XDG_RUNTIME_DIR +.It Ev XDG_RUNTIME_DIR +A directory where temporary user files, such as the Wayland socket, +are stored. +.It Ev XDG_CONFIG_DIR +A directory containung configuration of various programs and +libraries, including libxkbcommon. +.It Ev DISPLAY , WAYLAND_DISPLAY , WAYLAND_SOCKET +Tell how to connect to an underlying X11 or Wayland server. +.It Ev WLR_* +Various variables specific to wlroots. +.It Ev XKB_* , XLOCALEDIR , XCOMPOSEFILE +Various variables specific to libxkbcommon. +.It Ev XCURSOR_PATH +List of directories to search for XCursor themes in. +.It Ev HOME +A directory where there are always dear files there for you. +Waiting for you to clean them up. +.El +.Pp +These are set by +.Nm : +.Bl -tag -width WAYLAND_DISPLAY +.It Ev WAYLAND_DISPLAY +Tell how to connect to +.Nm . +.It Ev DISPLAY +If using +.Nm Xwayland , +tell how to connect to the +.Nm Xwayland +server. +.El +.Sh EXAMPLES +Start +.Nm +with s6 in the background: +.Dl dwl -s 's6-svscan <&-' +.Sh SEE ALSO +.Xr foot 1 , +.Xr bemenu 1 , +.Xr dwm 1 , +.Xr xkeyboard-config 7 +.Sh CAVEATS +The child process's standard input is connected with a pipe to +.Nm . +If the child process neither reads from the pipe nor closes its +standard input, +.Nm +will freeze after a while due to it blocking when writing to the full +pipe buffer. +.Sh BUGS +All of them. diff --git a/dwl.c b/dwl.c new file mode 100644 index 0000000..44ab938 --- /dev/null +++ b/dwl.c @@ -0,0 +1,5361 @@ +/* + * See LICENSE file for copyright and license details. + */ +#define XWAYLNAD 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XWAYLAND +#include +#include +#include +#endif + +#include "dwl-ipc-unstable-v2-protocol.h" +#include "util.h" +#include "wlr_foreign_toplevel_management_v1.h" + + +/* macros */ +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) +#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define END(A) ((A) + LENGTH(A)) +#define TAGMASK ((1 << LENGTH(tags)) - 1) +#define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) +#define ISFULLSCREEN(A) ((A)->isfullscreen || (A)->isfakefullscreen || (A)->overview_isfakefullscreenbak || (A)->overview_isfullscreenbak) +#define LISTEN_STATIC(E, H) do { static struct wl_listener _l = {.notify = (H)}; wl_signal_add((E), &_l); } while (0) + +/* enums */ +/* enums */ +enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ +enum { XDGShell, LayerShell, X11 }; /* client types */ +enum { AxisUp, AxisRight, AxisDown, AxisLeft }; //滚轮滚动的方向 +enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrFS, LyrTop, LyrOverlay, +#ifdef IM + LyrIMPopup, +#endif + LyrBlock, NUM_LAYERS }; /* scene layers */ +#ifdef XWAYLAND +enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, + NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ +#endif +enum { UP, DOWN, LEFT, RIGHT }; /* movewin */ + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct { + unsigned int mod; + unsigned int button; + void (*func)(const Arg *); + const Arg arg; +} Button; //鼠标按键 + +typedef struct { + unsigned int mod; + unsigned int dir; + void (*func)(const Arg *); + const Arg arg; +} Axis; + +struct dwl_animation { + bool should_animate; + bool running; + uint32_t total_frames; + uint32_t passed_frames; + struct wlr_box initial; + struct wlr_box current; +}; + + +typedef struct Pertag Pertag; +typedef struct Monitor Monitor; +typedef struct { + /* Must keep these three elements in this order */ + unsigned int type; /* XDGShell or X11* */ + struct wlr_box geom,oldgeom,animainit_geom,current; /* layout-relative, includes border */ + Monitor *mon; + struct wlr_scene_tree *scene; + struct wlr_scene_rect *border[4]; /* top, bottom, left, right */ + struct wlr_scene_tree *scene_surface; + struct wl_list link; + struct wl_list flink; + union { + struct wlr_xdg_surface *xdg; + struct wlr_xwayland_surface *xwayland; + } surface; + struct wl_listener commit; + struct wl_listener map; + struct wl_listener maximize; + struct wl_listener minimize; + struct wl_listener unmap; + struct wl_listener destroy; + struct wl_listener set_title; + struct wl_listener fullscreen; + struct wlr_box prev; /* layout-relative, includes border */ +#ifdef XWAYLAND + struct wl_listener activate; + struct wl_listener associate; + struct wl_listener dissociate; + struct wl_listener configure; + struct wl_listener set_hints; +#endif + unsigned int bw; + unsigned int tags,oldtags; + bool dirty; + uint32_t configure_serial; + struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; + int isfloating, isurgent, isfullscreen, istiled, isminied; + int isfakefullscreen; + int overview_backup_x, overview_backup_y, overview_backup_w, + overview_backup_h, overview_backup_bw; + int fullscreen_backup_x, fullscreen_backup_y, fullscreen_backup_w, + fullscreen_backup_h; + int overview_isfullscreenbak,overview_isfakefullscreenbak,overview_isfloatingbak; + uint32_t resize; /* configure serial of a pending resize */ + + struct wlr_xdg_toplevel_decoration_v1 *decoration; + struct wl_listener foreign_activate_request; + struct wl_listener foreign_fullscreen_request; + struct wl_listener foreign_close_request; + struct wl_listener foreign_destroy; + struct wl_listener set_decoration_mode; + struct wl_listener destroy_decoration; + + unsigned int ignore_clear_fullscreen; + int isnoclip; + int is_in_scratchpad; + int is_scratchpad_show; + int isglobal; + int isnoborder; + int iskilling; + struct wlr_box bounds; + bool resizing; + bool is_first_resize; + + struct dwl_animation animation; + +} Client; + +typedef struct { + struct wl_list link; + struct wl_resource *resource; + Monitor *monitor; +} DwlIpcOutput; + +typedef struct { + uint32_t mod; + xkb_keysym_t keysym; + void (*func)(const Arg *); + const Arg arg; +} Key; + +typedef struct { + struct wl_list link; + struct wlr_keyboard *wlr_keyboard; + + int nsyms; + const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */ + uint32_t mods; /* invalid if nsyms == 0 */ + struct wl_event_source *key_repeat_source; + + struct wl_listener modifiers; + struct wl_listener key; + struct wl_listener destroy; +} Keyboard; + +typedef struct { + /* Must keep these three elements in this order */ + unsigned int type; /* LayerShell */ + struct wlr_box geom; + Monitor *mon; + struct wlr_scene_tree *scene; + struct wlr_scene_tree *popups; + struct wlr_scene_layer_surface_v1 *scene_layer; + struct wl_list link; + int mapped; + struct wlr_layer_surface_v1 *layer_surface; + + struct wl_listener destroy; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener surface_commit; +} LayerSurface; + +typedef struct { + const char *symbol; + void (*arrange)(Monitor *,unsigned int,unsigned int); +} Layout; + +struct Monitor { + struct wl_list link; + struct wlr_output *wlr_output; + struct wlr_scene_output *scene_output; + // struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ + struct wl_listener frame; + struct wl_listener destroy; + struct wl_listener request_state; + struct wl_listener destroy_lock_surface; + struct wlr_session_lock_surface_v1 *lock_surface; + struct wlr_box m; /* monitor area, layout-relative */ + struct wlr_box w; /* window area, layout-relative */ + struct wl_list layers[4]; /* LayerSurface::link */ + const Layout *lt[2]; + unsigned int seltags; + unsigned int sellt; + uint32_t tagset[2]; + double mfact; + int nmaster; + char ltsymbol[16]; + + struct wl_list dwl_ipc_outputs; + int gappih; /* horizontal gap between windows */ + int gappiv; /* vertical gap between windows */ + int gappoh; /* horizontal outer gaps */ + int gappov; /* vertical outer gaps */ + Pertag *pertag; + Client *sel; + int isoverview; + int is_in_hotarea; + int gamma_lut_changed; +}; + +typedef struct { + const char *name; + float mfact; + int nmaster; + float scale; + const Layout *lt; + enum wl_output_transform rr; + int x, y; +} MonitorRule; + +typedef struct { + struct wlr_pointer_constraint_v1 *constraint; + struct wl_listener destroy; +} PointerConstraint; + +typedef struct { + const char *id; + const char *title; + unsigned int tags; + int isfloating; + int isfullscreen; + int isnoclip; + int isnoborder; + int monitor; + unsigned int width; + unsigned int height; +} Rule; + +typedef struct { + struct wlr_scene_tree *scene; + + struct wlr_session_lock_v1 *lock; + struct wl_listener new_surface; + struct wl_listener unlock; + struct wl_listener destroy; +} SessionLock; + +/* function declarations */ +static void logtofile(const char *fmt, ...); //日志函数 +static void lognumtofile(unsigned int num); //日志函数 +static void applybounds(Client *c, struct wlr_box *bbox); //设置边界规则,能让一些窗口拥有比较适合的大小 +static void applyrules(Client *c); //窗口规则应用,应用config.h中定义的窗口规则 +static void arrange(Monitor *m); //布局函数,让窗口俺平铺规则移动和重置大小 +static void arrangelayer(Monitor *m, struct wl_list *list, +struct wlr_box *usable_area, int exclusive); +static void arrangelayers(Monitor *m); +static void autostartexec(void); //自启动命令执行 +static void axisnotify(struct wl_listener *listener, void *data); //滚轮事件处理 +static void buttonpress(struct wl_listener *listener, void *data); //鼠标按键事件处理 +static void chvt(const Arg *arg); +static void checkidleinhibitor(struct wlr_surface *exclude); +static void cleanup(void); //退出清理 +static void cleanupkeyboard(struct wl_listener *listener, void *data); //退出清理 +static void cleanupmon(struct wl_listener *listener, void *data); //退出清理 +static void closemon(Monitor *m); //退出清理 +static void toggle_hotarea(int x_root, int y_root); //触发热区 +static void commitlayersurfacenotify(struct wl_listener *listener, void *data); +static void commitnotify(struct wl_listener *listener, void *data); +static void createdecoration(struct wl_listener *listener, void *data); +static void createidleinhibitor(struct wl_listener *listener, void *data); +static void createkeyboard(struct wlr_keyboard *keyboard); +static void requestmonstate(struct wl_listener *listener, void *data); +static void createlayersurface(struct wl_listener *listener, void *data); +static void createlocksurface(struct wl_listener *listener, void *data); +static void createmon(struct wl_listener *listener, void *data); +static void createnotify(struct wl_listener *listener, void *data); +static void createpointer(struct wlr_pointer *pointer); +static void createpointerconstraint(struct wl_listener *listener, void *data); +static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); +static void cursorframe(struct wl_listener *listener, void *data); +static void cursorwarptohint(void); +static void destroydecoration(struct wl_listener *listener, void *data); +static void defaultgaps(const Arg *arg); +static void destroydragicon(struct wl_listener *listener, void *data); +static void destroyidleinhibitor(struct wl_listener *listener, void *data); +static void destroylayersurfacenotify(struct wl_listener *listener, void *data); +static void destroylock(SessionLock *lock, int unlocked); +static void destroylocksurface(struct wl_listener *listener, void *data); +static void destroynotify(struct wl_listener *listener, void *data); +static void destroypointerconstraint(struct wl_listener *listener, void *data); +static void destroysessionlock(struct wl_listener *listener, void *data); +static void destroysessionmgr(struct wl_listener *listener, void *data); +static Monitor *dirtomon(enum wlr_direction dir); +static void setcursorshape(struct wl_listener *listener, void *data); +static void dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id); +static void dwl_ipc_manager_destroy(struct wl_resource *resource); +static void dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output); +static void dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource); +static void dwl_ipc_output_destroy(struct wl_resource *resource); +static void dwl_ipc_output_printstatus(Monitor *monitor); +static void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output); +static void dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags); +static void dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index); +static void dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset); +static void dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource); +static void focusclient(Client *c, int lift); +static void focusmon(const Arg *arg); +static void focusstack(const Arg *arg); +static void setborder_color(Client *c); +static Client *focustop(Monitor *m); +static void fullscreennotify(struct wl_listener *listener, void *data); +static void incnmaster(const Arg *arg); +static void incgaps(const Arg *arg); +static void incigaps(const Arg *arg); +static int keyrepeat(void *data); +static void incihgaps(const Arg *arg); +static void incivgaps(const Arg *arg); +static void incogaps(const Arg *arg); +static void incohgaps(const Arg *arg); +static void incovgaps(const Arg *arg); +static void toggleglobal(const Arg *arg); +static void inputdevice(struct wl_listener *listener, void *data); +static int keybinding(uint32_t mods, xkb_keysym_t sym); +static void keypress(struct wl_listener *listener, void *data); +static void keypressmod(struct wl_listener *listener, void *data); +static void killclient(const Arg *arg); +static void locksession(struct wl_listener *listener, void *data); +static void maplayersurfacenotify(struct wl_listener *listener, void *data); +static void mapnotify(struct wl_listener *listener, void *data); +static void maximizenotify(struct wl_listener *listener, void *data); +static void minimizenotify(struct wl_listener *listener, void *data); +static void monocle(Monitor *m); +static void motionabsolute(struct wl_listener *listener, void *data); +static void motionnotify(uint32_t time, struct wlr_input_device *device, double sx, + double sy, double sx_unaccel, double sy_unaccel); +static void motionrelative(struct wl_listener *listener, void *data); +static void moveresize(const Arg *arg); +static void exchange_client(const Arg *arg); +static void reset_foreign_tolevel(Client *c); +static void exchange_two_client(Client *c1, Client *c2); +static void outputmgrapply(struct wl_listener *listener, void *data); +static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test); +static void outputmgrtest(struct wl_listener *listener, void *data); +static void pointerfocus(Client *c, struct wlr_surface *surface, + double sx, double sy, uint32_t time); +static void printstatus(void); +static void quit(const Arg *arg); +static void quitsignal(int signo); +static void rendermon(struct wl_listener *listener, void *data); +static void requestdecorationmode(struct wl_listener *listener, void *data); +static void requeststartdrag(struct wl_listener *listener, void *data); +static void resize(Client *c, struct wlr_box geo, int interact); +static void resizeclient(Client *c,int x,int y,int w,int h, int interact); +static void run(char *startup_cmd); +static void setcursor(struct wl_listener *listener, void *data); +static void setfloating(Client *c, int floating); +static void setfullscreen(Client *c, int fullscreen); +static void setfakefullscreen(Client *c, int fakefullscreen); +static void setgaps(int oh, int ov, int ih, int iv); +static void setlayout(const Arg *arg); +static void switch_layout(const Arg *arg); +static void setmfact(const Arg *arg); +static void setmon(Client *c, Monitor *m, unsigned int newtags); +static void setpsel(struct wl_listener *listener, void *data); +static void setsel(struct wl_listener *listener, void *data); +static void setup(void); +static void sigchld(int unused); +static void spawn(const Arg *arg); +static void startdrag(struct wl_listener *listener, void *data); +static void tag(const Arg *arg); +static void tagmon(const Arg *arg); +static void setgamma(struct wl_listener *listener, void *data); +static void tile(Monitor *m, unsigned int gappo, unsigned int uappi); +static void overview(Monitor *m, unsigned int gappo, unsigned int gappi); +static void grid(Monitor *m, unsigned int gappo, unsigned int uappi); +static void togglefloating(const Arg *arg); +static void togglefullscreen(const Arg *arg); +static void togglefakefullscreen(const Arg *arg); +static void togglegaps(const Arg *arg); +static void toggletag(const Arg *arg); +static void toggleview(const Arg *arg); +static void unlocksession(struct wl_listener *listener, void *data); +static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); +static void unmapnotify(struct wl_listener *listener, void *data); +static void updatemons(struct wl_listener *listener, void *data); +static void updatetitle(struct wl_listener *listener, void *data); +static void urgent(struct wl_listener *listener, void *data); +static void view(const Arg *arg); +static void viewtoleft_have_client(const Arg *arg); +static void viewtoright_have_client(const Arg *arg); +static void viewtoleft(const Arg *arg); +static void viewtoright(const Arg *arg); +static void handlesig(int signo); +static void tagtoleft(const Arg *arg); +static void tagtoright(const Arg *arg); +static void virtualkeyboard(struct wl_listener *listener, void *data); +static void virtualpointer(struct wl_listener *listener, void *data); +static void warp_cursor(const Client *c); +static Monitor *xytomon(double x, double y); +static void xytonode(double x, double y, struct wlr_surface **psurface, + Client **pc, LayerSurface **pl, double *nx, double *ny); +static void zoom(const Arg *arg); +static void clear_fullscreen_flag(Client *c); +static Client *direction_select(const Arg *arg); +static void focusdir(const Arg *arg); +static void toggleoverview(const Arg *arg); +static void warp_cursor_to_selmon(const Monitor *m); +unsigned int want_restore_fullscreen(Client *target_client); +static void overview_restore(Client *c, const Arg *arg); +static void overview_backup(Client *c); +static void applyrulesgeom(Client *c); +static void set_minized(Client *c); +static void minized(const Arg *arg); +static void restore_minized(const Arg *arg); +static void toggle_scratchpad(const Arg *arg); +static void show_scratchpad(Client *c); +static void show_hide_client(Client *c); +static void tag_client(const Arg *arg, Client *target_client); + +static void handle_foreign_activate_request(struct wl_listener *listener, void *data); +static void handle_foreign_fullscreen_request(struct wl_listener *listener, void *data); +static void handle_foreign_close_request(struct wl_listener *listener, void *data); +static void handle_foreign_destroy(struct wl_listener *listener, void *data); + +static struct wlr_box setclient_coordinate_center(struct wlr_box geom); +static unsigned int get_tags_first_tag(unsigned int tags); + +void client_commit(Client *c); +void apply_border(Client *c,struct wlr_box clip_box); + +/* variables */ +static const char broken[] = "broken"; +// static const char *cursor_image = "left_ptr"; +static pid_t child_pid = -1; +static int locked; +static void *exclusive_focus; +static struct wl_display *dpy; +static struct wlr_relative_pointer_manager_v1 *pointer_manager; +static struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; +static struct wlr_backend *backend; +static struct wlr_scene *scene; +static struct wlr_scene_tree *layers[NUM_LAYERS]; +static struct wlr_renderer *drw; +static struct wlr_allocator *alloc; +static struct wlr_compositor *compositor; + +static struct wlr_gamma_control_manager_v1 *gamma_control_mgr; +static struct wlr_xdg_shell *xdg_shell; +static struct wlr_xdg_activation_v1 *activation; +static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr; +static struct wl_list clients; /* tiling order */ +static struct wl_list fstack; /* focus order */ +// static struct wlr_idle *idle; +static struct wlr_idle_notifier_v1 *idle_notifier; +static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; +// static struct wlr_input_inhibit_manager *input_inhibit_mgr; +static struct wlr_layer_shell_v1 *layer_shell; +static struct wlr_output_manager_v1 *output_mgr; +static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; +static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr; + +static struct wlr_cursor *cursor; +static struct wlr_xcursor_manager *cursor_mgr; +static struct wlr_session *session; + +static struct wlr_session_lock_manager_v1 *session_lock_mgr; +static struct wlr_scene_rect *locked_bg; +static struct wlr_session_lock_v1 *cur_lock; +static const int layermap[] = { LyrBg, LyrBottom, LyrTop, LyrOverlay }; +static struct wlr_scene_tree *drag_icon; +static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr; //这个跟steup obs影响对应 +static struct wlr_pointer_constraints_v1 *pointer_constraints; +static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr; +static struct wlr_pointer_constraint_v1 *active_constraint; + +static struct wlr_seat *seat; +static struct wl_list keyboards; +static unsigned int cursor_mode; +static Client *grabc; +static int grabcx, grabcy; /* client-relative */ + +static struct wlr_output_layout *output_layout; +static struct wlr_box sgeom; +static struct wl_list mons; +static Monitor *selmon; + +static int enablegaps = 1; /* enables gaps, used by togglegaps */ +static int axis_apply_time = 0; + +/* global event handlers */ +static struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = {.release = dwl_ipc_manager_release, .get_output = dwl_ipc_manager_get_output}; +static struct zdwl_ipc_output_v2_interface dwl_output_implementation = {.release = dwl_ipc_output_release, .set_tags = dwl_ipc_output_set_tags, .set_layout = dwl_ipc_output_set_layout, .set_client_tags = dwl_ipc_output_set_client_tags}; +static struct wl_listener lock_listener = {.notify = locksession}; + +#ifdef XWAYLAND +static void activatex11(struct wl_listener *listener, void *data); +static void configurex11(struct wl_listener *listener, void *data); +static void createnotifyx11(struct wl_listener *listener, void *data); +static void dissociatex11(struct wl_listener *listener, void *data); +static void associatex11(struct wl_listener *listener, void *data); +static Atom getatom(xcb_connection_t *xc, const char *name); +static void sethints(struct wl_listener *listener, void *data); +static void xwaylandready(struct wl_listener *listener, void *data); +// static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11}; +// static struct wl_listener xwayland_ready = {.notify = xwaylandready}; +static struct wlr_xwayland *xwayland; +static Atom netatom[NetLast]; +#endif + +/* configuration, allows nested code to access above variables */ +#include "config.h" + +/* attempt to encapsulate suck into one file */ +#include "client.h" +#ifdef IM +#include "IM.h" +#endif + +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +struct Pertag { + unsigned int curtag, prevtag; /* current and previous tag */ + int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ + float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ + unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ + const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ +}; + +static pid_t *autostart_pids; +static size_t autostart_len; + +struct vec2 { + double x, y; +}; + +#define BAKED_POINTS_COUNT 256 +struct vec2 *baked_points; + +struct vec2 +calculate_animation_curve_at(double t) { + struct vec2 point; + + point.x = 3 * t * (1 - t) * (1 - t) * animation_curve[0] + + 3 * t * t * (1 - t) * animation_curve[2] + + t * t * t; + + point.y = 3 * t * (1 - t) * (1 - t) * animation_curve[1] + + 3 * t * t * (1 - t) * animation_curve[3] + + t * t * t; + + return point; +} + +void init_baked_points() { + baked_points = calloc(BAKED_POINTS_COUNT, sizeof(*baked_points)); + + for(size_t i = 0; i < BAKED_POINTS_COUNT; i++) { + baked_points[i] = calculate_animation_curve_at((double)i / (BAKED_POINTS_COUNT - 1)); + } +} + +double +find_animation_curve_at(double t) { + size_t down = 0; + size_t up = BAKED_POINTS_COUNT - 1; + + size_t middle = (up + down) / 2; + while(up - down != 1) { + if(baked_points[middle].x <= t) { + down = middle; + } else { + up = middle; + } + middle = (up + down) / 2; + } + + return baked_points[up].y; +} + +bool +client_animation_next_tick(Client *c) +{ + double animation_passed = + (double)c->animation.passed_frames / c->animation.total_frames; + double factor = find_animation_curve_at(animation_passed); + + uint32_t width = c->animation.initial.width + + (c->current.width - c->animation.initial.width) * factor; + uint32_t height = c->animation.initial.height + + (c->current.height - c->animation.initial.height) * factor; + + uint32_t x = c->animation.initial.x + + (c->current.x - c->animation.initial.x) * factor; + uint32_t y = c->animation.initial.y + + (c->current.y - c->animation.initial.y) * factor; + + wlr_scene_node_set_position(&c->scene->node, x, y); + c->is_first_resize = false; + c->animation.current = (struct wlr_box){ + .x = x, + .y = y, + .width = width, + .height = height, + }; + + if (animation_passed == 1.0) + { + c->animation.running = false; + return false; + } + else + { + c->animation.passed_frames++; + return true; + } +} + +void +client_actual_size(Client *c, uint32_t *width, uint32_t *height) +{ + *width = c->animation.running + ? MIN(c->animation.current.width, c->current.width) + : c->current.width; + + *height = c->animation.running + ? MIN(c->animation.current.height, c->current.height) + : c->current.height; +} + +void +apply_border(Client *c,struct wlr_box clip_box) { + wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); + wlr_scene_rect_set_size(c->border[0], clip_box.width, c->bw); + wlr_scene_rect_set_size(c->border[1], clip_box.width, c->bw); + wlr_scene_rect_set_size(c->border[2], c->bw, clip_box.height - 2 * c->bw); + wlr_scene_rect_set_size(c->border[3], c->bw, clip_box.height - 2 * c->bw); + wlr_scene_node_set_position(&c->border[1]->node, 0, clip_box.height - c->bw); + wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw); + wlr_scene_node_set_position(&c->border[3]->node, clip_box.width - c->bw, c->bw); + wlr_scene_node_set_position(&c->border[1]->node, 0, clip_box.height - c->bw); + wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw); + wlr_scene_node_set_position(&c->border[3]->node, clip_box.width - c->bw, c->bw); +} + +void +client_apply_clip(Client *c) +{ + uint32_t width, height; + client_actual_size(c, &width, &height); + + struct wlr_box geometry; + client_get_geometry(c,&geometry); + struct wlr_box clip_box = (struct wlr_box){ + .x = geometry.x, + .y = geometry.y, + .width = width, + .height = height, + }; + + if (client_is_x11(c)) + { + clip_box.x = 0; + clip_box.y = 0; + } + + wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box); + apply_border(c, clip_box); +} + +bool client_draw_frame(Client *c) +{ + if (!c || !c->mon || !client_surface(c)->mapped) + return false; + if (!VISIBLEON(c, c->mon)) + return false; + bool need_more_frames = false; + if (c->animation.running) { + if (client_animation_next_tick(c)) + { + need_more_frames = true; + } + } else { + wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); + apply_border(c, c->geom); + } + + client_apply_clip(c); + + return need_more_frames; +} + +void //0.5 +applybounds(Client *c, struct wlr_box *bbox) +{ + /* set minimum possible */ + c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width); + c->geom.height = MAX(1 + 2 * (int)c->bw, c->geom.height); + + if (c->geom.x >= bbox->x + bbox->width) + c->geom.x = bbox->x + bbox->width - c->geom.width; + if (c->geom.y >= bbox->y + bbox->height) + c->geom.y = bbox->y + bbox->height - c->geom.height; + if (c->geom.x + c->geom.width <= bbox->x) + c->geom.x = bbox->x; + if (c->geom.y + c->geom.height <= bbox->y) + c->geom.y = bbox->y; +} + +/*清除全屏标志,还原全屏时清0的border*/ +void clear_fullscreen_flag(Client *c) { + if (c->isfullscreen || c->isfakefullscreen) { + c->isfullscreen = 0; + c->isfloating = 0; + c->isfakefullscreen = 0; + c->bw = borderpx; + client_set_fullscreen(c, false); + } +} + +void //升级忽略 +setgamma(struct wl_listener *listener, void *data) +{ + struct wlr_gamma_control_manager_v1_set_gamma_event *event = data; + struct wlr_output_state state; + wlr_output_state_init(&state); + if (!wlr_gamma_control_v1_apply(event->control, &state)) { + wlr_output_state_finish(&state); + return; + } + + if (!wlr_output_test_state(event->output, &state)) { + wlr_gamma_control_v1_send_failed_and_destroy(event->control); + wlr_output_state_finish(&state); + return; + } + + wlr_output_commit_state(event->output, &state); + wlr_output_schedule_frame(event->output); +} + +void minized(const Arg *arg) { + if(selmon->sel && !selmon->sel->isminied) { + set_minized(selmon->sel); + } +} + +void restore_minized(const Arg *arg) { + Client *c; + wl_list_for_each(c, &clients, link) { + if (c->isminied) { + show_hide_client(c); + c->is_scratchpad_show = 0; + c->is_in_scratchpad = 0; + setborder_color(c); + break; + } + } + +} + +void show_scratchpad(Client *c) { + c->is_scratchpad_show = 1; + if (c->isfullscreen || c->isfakefullscreen) { + c->isfullscreen = 0; // 清除窗口全屏标志 + c->isfakefullscreen = 0; + c->bw = borderpx; // 恢复非全屏的border + } + /* return if fullscreen */ + if(!c->isfloating) { + setfloating(c, 1); + c->geom.width = c->mon->w.width * 0.7; + c->geom.height = c->mon->w.height * 0.8; + //重新计算居中的坐标 + c->geom = setclient_coordinate_center(c->geom); + resize(c, c->geom, 0); + } + c->oldtags = selmon->tagset[selmon->seltags]; + show_hide_client(c); + setborder_color(c); +} + +void reset_foreign_tolevel(Client *c) { + wlr_foreign_toplevel_handle_v1_destroy(c->foreign_toplevel); + c->foreign_toplevel = NULL; + // printstatus(); + //创建外部顶层窗口的句柄,每一个顶层窗口都有一个 + c->foreign_toplevel = wlr_foreign_toplevel_handle_v1_create(foreign_toplevel_manager); + //监听来自外部对于窗口的事件请求 + if(c->foreign_toplevel){ + LISTEN(&(c->foreign_toplevel->events.request_activate), + &c->foreign_activate_request,handle_foreign_activate_request); + LISTEN(&(c->foreign_toplevel->events.request_fullscreen), + &c->foreign_fullscreen_request,handle_foreign_fullscreen_request); + LISTEN(&(c->foreign_toplevel->events.request_close), + &c->foreign_close_request,handle_foreign_close_request); + LISTEN(&(c->foreign_toplevel->events.destroy), + &c->foreign_destroy,handle_foreign_destroy); + //设置外部顶层句柄的id为应用的id + const char *appid; + appid = client_get_appid(c) ; + if(appid) + wlr_foreign_toplevel_handle_v1_set_app_id(c->foreign_toplevel,appid); + //设置外部顶层句柄的title为应用的title + const char *title; + title = client_get_title(c) ; + if(title) + wlr_foreign_toplevel_handle_v1_set_title(c->foreign_toplevel,title); + //设置外部顶层句柄的显示监视器为当前监视器 + wlr_foreign_toplevel_handle_v1_output_enter( + c->foreign_toplevel, c->mon->wlr_output); + } +} + +void toggle_scratchpad(const Arg *arg) { + Client *c; + wl_list_for_each(c, &clients, link) { + if(c->mon != selmon) { + continue; + } + if(c->is_in_scratchpad && c->is_scratchpad_show && (selmon->tagset[selmon->seltags] & c->tags) == 0 ) { + unsigned int target = get_tags_first_tag(selmon->tagset[selmon->seltags]); + tag_client(&(Arg){.ui = target},c); + return; + } else if (c->is_in_scratchpad && c->is_scratchpad_show && (selmon->tagset[selmon->seltags] & c->tags) != 0) { + set_minized(c); + return; + } else if ( c && c->is_in_scratchpad && !c->is_scratchpad_show) { + show_scratchpad(c); + return; + } + } +} + +void //0.5 +handlesig(int signo) +{ + if (signo == SIGCHLD) { +#ifdef XWAYLAND + siginfo_t in; + /* wlroots expects to reap the XWayland process itself, so we + * use WNOWAIT to keep the child waitable until we know it's not + * XWayland. + */ + while (!waitid(P_ALL, 0, &in, WEXITED|WNOHANG|WNOWAIT) && in.si_pid + && (!xwayland || in.si_pid != xwayland->server->pid)) + waitpid(in.si_pid, NULL, 0); +#else + while (waitpid(-1, NULL, WNOHANG) > 0); +#endif + } else if (signo == SIGINT || signo == SIGTERM) { + quit(NULL); + } +} + +void toggle_hotarea(int x_root, int y_root) { + // 左下角热区坐标计算,兼容多显示屏 + Arg arg = {0}; + unsigned hx = selmon->m.x + hotarea_size; + unsigned hy = selmon->m.y + selmon->m.height - hotarea_size; + + if (enable_hotarea == 1 && selmon->is_in_hotarea == 0 && y_root > hy && + x_root < hx && x_root >= selmon->m.x && + y_root <= (selmon->m.y + selmon->m.height)) { + toggleoverview(&arg); + selmon->is_in_hotarea = 1; + } else if (enable_hotarea == 1 && selmon->is_in_hotarea == 1 && + (y_root <= hy || x_root >= hx || x_root < selmon->m.x || + y_root > (selmon->m.y + selmon->m.height))) { + selmon->is_in_hotarea = 0; + } +} + +struct wlr_box //计算客户端居中坐标 +setclient_coordinate_center(struct wlr_box geom){ + struct wlr_box tempbox; + tempbox.x = selmon->w.x + (selmon->w.width - geom.width) / 2; + tempbox.y = selmon->w.y + (selmon->w.height - geom.height) / 2; + tempbox.width = geom.width; + tempbox.height = geom.height; + return tempbox; + +} + +/* function implementations */ +void logtofile(const char *fmt, ...) { + char buf[256]; + char cmd[256]; + va_list ap; + va_start(ap, fmt); + vsprintf((char *)buf, fmt, ap); + va_end(ap); + unsigned int i = strlen((const char *)buf); + + sprintf(cmd, "echo '%.*s' >> ~/log", i, buf); + system(cmd); +} + +/* function implementations */ +void lognumtofile(unsigned int num) { + char cmd[256]; + sprintf(cmd, "echo '%x' >> ~/log", num); + system(cmd); +} + +void // 0.5 +applyrulesgeom(Client *c) +{ + /* rule matching */ + const char *appid, *title; + const Rule *r; + + if (!(appid = client_get_appid(c))) + appid = broken; + if (!(title = client_get_title(c))) + title = broken; + + for (r = rules; r < END(rules); r++) { + if ((!r->title || strstr(title, r->title)) + && (!r->id || strstr(appid, r->id)) && r->width !=0 && r->height != 0) { + c->geom.width = r->width; + c->geom.height = r->height; + //重新计算居中的坐标 + c->geom = setclient_coordinate_center(c->geom); + break; + } + } +} + +void // 17 +applyrules(Client *c) +{ + /* rule matching */ + const char *appid, *title; + uint32_t i, newtags = 0; + const Rule *r; + Monitor *mon = selmon, *m; + + c->isfloating = client_is_float_type(c); + if (!(appid = client_get_appid(c))) + appid = broken; + if (!(title = client_get_title(c))) + title = broken; + + for (r = rules; r < END(rules); r++) { + if ((!r->title || strstr(title, r->title)) + && (!r->id || strstr(appid, r->id))) { + c->isfloating = r->isfloating; + c->isnoclip = r->isnoclip; + c->isnoborder = r->isnoborder; + newtags |= r->tags; + i = 0; + wl_list_for_each(m, &mons, link) + if (r->monitor == i++) + mon = m; + + if(c->isfloating && r->width != 0 && r->height != 0){ + c->geom.width = r->width; + c->geom.height = r->height; + //重新计算居中的坐标 + c->geom = setclient_coordinate_center(c->geom); + } + if(r->isfullscreen){ + c->isfullscreen =1; + c->ignore_clear_fullscreen = 1; + } + break; + } + } + + wlr_scene_node_reparent(&c->scene->node, layers[c->isfloating ? LyrFloat : LyrTile]); + setmon(c, mon, newtags); + + Client *fc; + // 如果当前的tag中有新创建的非悬浮窗口,就让当前tag中的全屏窗口退出全屏参与平铺 + wl_list_for_each(fc, &clients, link) + if (fc && !c->ignore_clear_fullscreen && c->tags & fc->tags && ISFULLSCREEN(fc) && !c->isfloating ) { + clear_fullscreen_flag(fc); + arrange(c->mon); + }else if(c->ignore_clear_fullscreen && c->isfullscreen){ + setfullscreen(c,1); + } + + if(!(c->tags & ( 1 << (selmon->pertag->curtag - 1) ))){ + view(&(Arg){.ui = c->tags}); + } +} + +void //17 +arrange(Monitor *m) +{ + Client *c; + + if (!m->wlr_output->enabled) + return; + + wl_list_for_each(c, &clients, link) { + if(c->mon == m && c->isglobal) { + c->tags = m->tagset[m->seltags]; + focusclient(c,1); + } + if (c->mon == m) { + wlr_scene_node_set_enabled(&c->scene->node, VISIBLEON(c, m)); + client_set_suspended(c, !VISIBLEON(c, m)); + } + } + + // 给全屏窗口设置背景为黑色 好像要跟LyrFS图层一起使用,我不用这个图层,所以注释掉 + // wlr_scene_node_set_enabled(&m->fullscreen_bg->node, + // (c = focustop(m)) && c->isfullscreen); + + + + if(m->isoverview){ + overviewlayout.arrange(m,0,0); + }else if (m && m->lt[m->sellt]->arrange){ + m->lt[m->sellt]->arrange(m,gappoh,0); + } + + #ifdef IM + if (input_relay && input_relay->popup) + input_popup_update(input_relay->popup); + #endif + motionnotify(0, NULL, 0, 0, 0, 0); + checkidleinhibitor(NULL); +} + +void //0.5 +arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive) +{ + LayerSurface *l; + struct wlr_box full_area = m->m; + + wl_list_for_each(l, list, link) { + struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; + + if (exclusive != (layer_surface->current.exclusive_zone > 0)) + continue; + + wlr_scene_layer_surface_v1_configure(l->scene_layer, &full_area, usable_area); + wlr_scene_node_set_position(&l->popups->node, l->scene->node.x, l->scene->node.y); + l->geom.x = l->scene->node.x; + l->geom.y = l->scene->node.y; + } +} + +Client *direction_select(const Arg *arg) { + Client *c,*tempClients[100]; + Client *tc = selmon->sel; + int last = -1; + + if(!tc) + return NULL; + + if (tc && + (tc->isfullscreen || tc->isfakefullscreen)) /* no support for focusstack with fullscreen windows */ + return NULL; + + wl_list_for_each(c, &clients, link) + if (c && (c->tags & c->mon->tagset[c->mon->seltags])){ + last++; + tempClients[last] = c; + } + if (last < 0) + return NULL; + int sel_x = tc->geom.x; + int sel_y = tc->geom.y; + long long int distance = LLONG_MAX; + // int temp_focus = 0; + Client *tempFocusClients = NULL; + + switch (arg->i) { + case UP: + for (int _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.y < sel_y && tempClients[_i]->geom.x == sel_x) { + int dis_x = tempClients[_i]->geom.x - sel_x; + int dis_y = tempClients[_i]->geom.y - sel_y; + long long int tmp_distance = dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + } + } + if (!tempFocusClients) { + for (int _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.y < sel_y ) { + int dis_x = tempClients[_i]->geom.x - sel_x; + int dis_y = tempClients[_i]->geom.y - sel_y; + long long int tmp_distance = dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + } + } + } + break; + case DOWN: + for (int _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.y > sel_y && tempClients[_i]->geom.x == sel_x) { + int dis_x = tempClients[_i]->geom.x - sel_x; + int dis_y = tempClients[_i]->geom.y - sel_y; + long long int tmp_distance = dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + } + } + if (!tempFocusClients) { + for (int _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.y > sel_y ) { + int dis_x = tempClients[_i]->geom.x - sel_x; + int dis_y = tempClients[_i]->geom.y - sel_y; + long long int tmp_distance = dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + } + } + } + break; + case LEFT: + for (int _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.x < sel_x && tempClients[_i]->geom.y == sel_y) { + int dis_x = tempClients[_i]->geom.x - sel_x; + int dis_y = tempClients[_i]->geom.y - sel_y; + long long int tmp_distance = dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + } + } + if(!tempFocusClients) { + for (int _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.x < sel_x ) { + int dis_x = tempClients[_i]->geom.x - sel_x; + int dis_y = tempClients[_i]->geom.y - sel_y; + long long int tmp_distance = dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + } + } + } + break; + case RIGHT: + for (int _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.x > sel_x && tempClients[_i]->geom.y == sel_y) { + int dis_x = tempClients[_i]->geom.x - sel_x; + int dis_y = tempClients[_i]->geom.y - sel_y; + long long int tmp_distance = dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + } + } + if(!tempFocusClients) { + for (int _i = 0; _i <= last; _i++) { + if (tempClients[_i]->geom.x > sel_x ) { + int dis_x = tempClients[_i]->geom.x - sel_x; + int dis_y = tempClients[_i]->geom.y - sel_y; + long long int tmp_distance = dis_x * dis_x + dis_y * dis_y; // 计算距离 + if (tmp_distance < distance) { + distance = tmp_distance; + tempFocusClients = tempClients[_i]; + } + } + } + } + break; + } + return tempFocusClients; +} + +void focusdir(const Arg *arg) { + Client *c; + c = direction_select(arg); + if(c) { + focusclient(c,1); + if(warpcursor) + warp_cursor(c); + } +} + +void //0.5 +arrangelayers(Monitor *m) +{ + int i; + struct wlr_box usable_area = m->m; + LayerSurface *l; + uint32_t layers_above_shell[] = { + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, + ZWLR_LAYER_SHELL_V1_LAYER_TOP, + }; + if (!m->wlr_output->enabled) + return; + + /* Arrange exclusive surfaces from top->bottom */ + for (i = 3; i >= 0; i--) + arrangelayer(m, &m->layers[i], &usable_area, 1); + + if (!wlr_box_equal(&usable_area, &m->w)) { + m->w = usable_area; + arrange(m); + } + + /* Arrange non-exlusive surfaces from top->bottom */ + for (i = 3; i >= 0; i--) + arrangelayer(m, &m->layers[i], &usable_area, 0); + + /* Find topmost keyboard interactive layer, if such a layer exists */ + for (i = 0; i < (int)LENGTH(layers_above_shell); i++) { + wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) { + if (locked || !l->layer_surface->current.keyboard_interactive || !l->mapped) + continue; + /* Deactivate the focused client. */ + focusclient(NULL, 0); + exclusive_focus = l; + client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat)); + return; + } + } +} + +void +autostartexec(void) { + const char *const *p; + size_t i = 0; + + /* count entries */ + for (p = autostart; *p; autostart_len++, p++) + while (*++p); + + autostart_pids = calloc(autostart_len, sizeof(pid_t)); + for (p = autostart; *p; i++, p++) { + if ((autostart_pids[i] = fork()) == 0) { + setsid(); + execvp(*p, (char *const *)p); + die("dwl: execvp %s:", *p); + } + /* skip arguments */ + while (*++p); + } +} + +void //鼠标滚轮事件 +axisnotify(struct wl_listener *listener, void *data) +{ + + + /* This event is forwarded by the cursor when a pointer emits an axis event, + * for example when you move the scroll wheel. */ + struct wlr_pointer_axis_event *event = data; + struct wlr_keyboard *keyboard; + uint32_t mods; + const Axis *a; + unsigned int adir; + // IDLE_NOTIFY_ACTIVITY; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + keyboard = wlr_seat_get_keyboard(seat); + + //获取当前按键的mask,比如alt+super或者alt+ctrl + mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + + if (event->orientation == WLR_AXIS_ORIENTATION_VERTICAL) + adir = event->delta > 0 ? AxisDown : AxisUp; + else + adir = event->delta > 0 ? AxisRight : AxisLeft; + + for (a = axes; a < END(axes); a++) { + if (CLEANMASK(mods) == CLEANMASK(a->mod) && //按键一致 + adir == a->dir && a->func) { //滚轮方向判断一致且处理函数存在 + if (event->time_msec - axis_apply_time > axis_bind_apply_timeout) { + a->func(&a->arg); + axis_apply_time = event->time_msec; + return; //如果成功匹配就不把这个滚轮事件传送给客户端了 + } else { + axis_apply_time = event->time_msec; + return; + } + + } + } + + /* TODO: allow usage of scroll whell for mousebindings, it can be implemented + * checking the event's orientation and the delta of the event */ + /* Notify the client with pointer focus of the axis event. */ + wlr_seat_pointer_notify_axis(seat, //滚轮事件发送给客户端也就是窗口 + event->time_msec, event->orientation, event->delta, + event->delta_discrete, event->source); + + +} + +void //鼠标按键事件 +buttonpress(struct wl_listener *listener, void *data) +{ + struct wlr_pointer_button_event *event = data; + struct wlr_keyboard *keyboard; + uint32_t mods; + Client *c; + const Button *b; + // IDLE_NOTIFY_ACTIVITY; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + + switch (event->state) { + case WLR_BUTTON_PRESSED: + cursor_mode = CurPressed; + selmon = xytomon(cursor->x, cursor->y); + if (locked) + break; + + /* Change focus if the button was _pressed_ over a client */ + xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL); + if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) + focusclient(c, 1); + + keyboard = wlr_seat_get_keyboard(seat); + mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + for (b = buttons; b < END(buttons); b++) { + if (CLEANMASK(mods) == CLEANMASK(b->mod) && + event->button == b->button && b->func && (selmon->isoverview == 1 || b->button == BTN_MIDDLE ) && c) { + b->func(&b->arg); + return; + } else if (CLEANMASK(mods) == CLEANMASK(b->mod) && + event->button == b->button && b->func && CLEANMASK(b->mod) != 0 ) { + b->func(&b->arg); + return; + + } + } + break; + case WLR_BUTTON_RELEASED: + /* If you released any buttons, we exit interactive move/resize mode. */ + if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { + cursor_mode = CurNormal; + /* Clear the pointer focus, this way if the cursor is over a surface + * we will send an enter event after which the client will provide us + * a cursor surface */ + wlr_seat_pointer_clear_focus(seat); + motionnotify(0, NULL, 0, 0, 0, 0); + /* Drop the window off on its new monitor */ + selmon = xytomon(cursor->x, cursor->y); + setmon(grabc, selmon, 0); + grabc = NULL; + return; + } else { + cursor_mode = CurNormal; + } + break; + } + /* If the event wasn't handled by the compositor, notify the client with + * pointer focus that a button press has occurred */ + wlr_seat_pointer_notify_button(seat, + event->time_msec, event->button, event->state); +} + +void //0.5 +chvt(const Arg *arg) +{ + wlr_session_change_vt(session, arg->ui); +} + +void // 0.5 +checkidleinhibitor(struct wlr_surface *exclude) +{ + int inhibited = 0, unused_lx, unused_ly; + struct wlr_idle_inhibitor_v1 *inhibitor; + wl_list_for_each(inhibitor, &idle_inhibit_mgr->inhibitors, link) { + struct wlr_surface *surface = wlr_surface_get_root_surface(inhibitor->surface); + struct wlr_scene_tree *tree = surface->data; + if (exclude != surface && (bypass_surface_visibility || (!tree + || wlr_scene_node_coords(&tree->node, &unused_lx, &unused_ly)))) { + inhibited = 1; + break; + } + } + + wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited); +} + +void //0.5 +setcursorshape(struct wl_listener *listener, void *data) +{ + struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data; + if (cursor_mode != CurNormal && cursor_mode != CurPressed) + return; + /* This can be sent by any client, so we check to make sure this one is + * actually has pointer focus first. If so, we can tell the cursor to + * use the provided cursor shape. */ + if (event->seat_client == seat->pointer_state.focused_client) + wlr_cursor_set_xcursor(cursor, cursor_mgr, + wlr_cursor_shape_v1_name(event->shape)); +} + + +void //17 +cleanup(void) +{ +#ifdef XWAYLAND + wlr_xwayland_destroy(xwayland); +#endif + wl_display_destroy_clients(dpy); + if (child_pid > 0) { + kill(child_pid, SIGTERM); + waitpid(child_pid, NULL, 0); + } + wlr_backend_destroy(backend); + wlr_scene_node_destroy(&scene->tree.node); + wlr_renderer_destroy(drw); + wlr_allocator_destroy(alloc); + wlr_xcursor_manager_destroy(cursor_mgr); + wlr_cursor_destroy(cursor); + wlr_output_layout_destroy(output_layout); + wlr_seat_destroy(seat); + wl_display_destroy(dpy); +} + +void //17 +cleanupkeyboard(struct wl_listener *listener, void *data) +{ + Keyboard *kb = wl_container_of(listener, kb, destroy); + + wl_event_source_remove(kb->key_repeat_source); + wl_list_remove(&kb->link); + wl_list_remove(&kb->modifiers.link); + wl_list_remove(&kb->key.link); + wl_list_remove(&kb->destroy.link); + free(kb); +} + +void //0.5 +cleanupmon(struct wl_listener *listener, void *data) +{ + Monitor *m = wl_container_of(listener, m, destroy); + LayerSurface *l, *tmp; + size_t i; + + /* m->layers[i] are intentionally not unlinked */ + for (i = 0; i < LENGTH(m->layers); i++) { + wl_list_for_each_safe(l, tmp, &m->layers[i], link) + wlr_layer_surface_v1_destroy(l->layer_surface); + } + + wl_list_remove(&m->destroy.link); + wl_list_remove(&m->frame.link); + wl_list_remove(&m->link); + wl_list_remove(&m->request_state.link); + m->wlr_output->data = NULL; + wlr_output_layout_remove(output_layout, m->wlr_output); + wlr_scene_output_destroy(m->scene_output); + + closemon(m); + // wlr_scene_node_destroy(&m->fullscreen_bg->node); + free(m); +} + + +void +closemon(Monitor *m) //0.5 +{ + /* update selmon if needed and + * move closed monitor's clients to the focused one */ + Client *c; + int i = 0, nmons = wl_list_length(&mons); + if (!nmons) { + selmon = NULL; + } else if (m == selmon) { + do /* don't switch to disabled mons */ + selmon = wl_container_of(mons.next, selmon, link); + while (!selmon->wlr_output->enabled && i++ < nmons); + } + + wl_list_for_each(c, &clients, link) { + if (c->isfloating && c->geom.x > m->m.width) + resize(c, (struct wlr_box){.x = c->geom.x - m->w.width, .y = c->geom.y, + .width = c->geom.width, .height = c->geom.height}, 0); + if (c->mon == m) { + setmon(c, selmon, c->tags); + reset_foreign_tolevel(c); + } + } + focusclient(focustop(selmon), 1); + printstatus(); +} + +void //0.5 +commitlayersurfacenotify(struct wl_listener *listener, void *data) +{ + LayerSurface *l = wl_container_of(listener, l, surface_commit); + struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; + struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->current.layer]]; + + if (layer_surface->current.committed == 0 && l->mapped == layer_surface->surface->mapped) + return; + l->mapped = layer_surface->surface->mapped; + + if (scene_layer != l->scene->node.parent) { + wlr_scene_node_reparent(&l->scene->node, scene_layer); + wl_list_remove(&l->link); + wl_list_insert(&l->mon->layers[layer_surface->current.layer], &l->link); + wlr_scene_node_reparent(&l->popups->node, (layer_surface->current.layer + < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer)); + } + + arrangelayers(l->mon); +} + +void client_set_pending_state(Client *c, uint32_t x, uint32_t y, + uint32_t width, uint32_t height) +{ + // assert(toplevel->mapped); + + struct wlr_box pending = { + .x = x, + .y = y, + .width = width, + .height = height, + }; + + c->geom = pending; + + if (!animations || c == grabc || (!c->is_first_resize && wlr_box_equal(&c->current, &pending))) + { + c->animation.should_animate = false; + } + else + { + c->animation.should_animate = true; + c->animation.initial = c->animainit_geom; + } + + client_commit(c); + c->dirty = true; +} + +double +output_frame_duration_ms(Client *c) { + return 1000000.0 / c->mon->wlr_output->refresh; +} + +void client_commit(Client *c) +{ + c->dirty = false; + c->current = c->geom; + + if (c->animation.should_animate) + { + c->animation.passed_frames = 0; + c->animation.total_frames = animation_duration / output_frame_duration_ms(c); + + c->animation.running = true; + c->animation.should_animate = false; + } + + wlr_output_schedule_frame(c->mon->wlr_output); +} + +void //0.5 +commitnotify(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, commit); + // don't know need to do what... + return; +} + +void //0.5 +destroydecoration(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, destroy_decoration); + + wl_list_remove(&c->destroy_decoration.link); + wl_list_remove(&c->set_decoration_mode.link); + motionnotify(0, NULL, 0, 0, 0, 0); +} + + +void //0.5 +createdecoration(struct wl_listener *listener, void *data) +{ + struct wlr_xdg_toplevel_decoration_v1 *deco = data; + Client *c = deco->toplevel->base->data; + c->decoration = deco; + + LISTEN(&deco->events.request_mode, &c->set_decoration_mode, requestdecorationmode); + LISTEN(&deco->events.destroy, &c->destroy_decoration, destroydecoration); + + requestdecorationmode(&c->set_decoration_mode, deco); +} + + +void //0.5 +createidleinhibitor(struct wl_listener *listener, void *data) +{ + struct wlr_idle_inhibitor_v1 *idle_inhibitor = data; + LISTEN_STATIC(&idle_inhibitor->events.destroy, destroyidleinhibitor); + + checkidleinhibitor(NULL); +} + +void //17 +createkeyboard(struct wlr_keyboard *keyboard) +{ + struct xkb_context *context; + struct xkb_keymap *keymap; + Keyboard *kb = keyboard->data = ecalloc(1, sizeof(*kb)); + kb->wlr_keyboard = keyboard; + + /* Prepare an XKB keymap and assign it to the keyboard. */ + context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + keymap = xkb_keymap_new_from_names(context, &xkb_rules, + XKB_KEYMAP_COMPILE_NO_FLAGS); + + wlr_keyboard_set_keymap(keyboard, keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(context); + wlr_keyboard_set_repeat_info(keyboard, repeat_rate, repeat_delay); + + if(numlockon == 1){ + uint32_t leds = 0 ; + xkb_mod_mask_t locked_mods = 0; + leds = leds | WLR_LED_NUM_LOCK; + //获取numlock所在的位置 + xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, + XKB_MOD_NAME_NUM); + if (mod_index != XKB_MOD_INVALID) { + locked_mods |= (uint32_t)1 << mod_index; //将该位置设置为1,默认为0表示锁住小键盘 + } + //设置numlock为on + xkb_state_update_mask(keyboard->xkb_state, 0, 0, + locked_mods, 0, 0, 0); + wlr_keyboard_led_update(keyboard,leds); //将表示numlockon的灯打开 + } + + /* Here we set up listeners for keyboard events. */ + LISTEN(&keyboard->events.modifiers, &kb->modifiers, keypressmod); + LISTEN(&keyboard->events.key, &kb->key, keypress); + LISTEN(&keyboard->base.events.destroy, &kb->destroy, cleanupkeyboard); + + wlr_seat_set_keyboard(seat, keyboard); + + kb->key_repeat_source = wl_event_loop_add_timer( + wl_display_get_event_loop(dpy), keyrepeat, kb); + + /* And add the keyboard to our list of keyboards */ + wl_list_insert(&keyboards, &kb->link); +} + +void //0.5 +createlayersurface(struct wl_listener *listener, void *data) +{ + struct wlr_layer_surface_v1 *layer_surface = data; + LayerSurface *l; + struct wlr_surface *surface = layer_surface->surface; + struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->pending.layer]]; + struct wlr_layer_surface_v1_state old_state; + + if (!layer_surface->output + && !(layer_surface->output = selmon ? selmon->wlr_output : NULL)) { + wlr_layer_surface_v1_destroy(layer_surface); + return; + } + + l = layer_surface->data = ecalloc(1, sizeof(*l)); + l->type = LayerShell; + LISTEN(&surface->events.commit, &l->surface_commit, commitlayersurfacenotify); + LISTEN(&surface->events.map, &l->map, maplayersurfacenotify); + LISTEN(&surface->events.unmap, &l->unmap, unmaplayersurfacenotify); + LISTEN(&layer_surface->events.destroy, &l->destroy, destroylayersurfacenotify); + + l->layer_surface = layer_surface; + l->mon = layer_surface->output->data; + l->scene_layer = wlr_scene_layer_surface_v1_create(scene_layer, layer_surface); + l->scene = l->scene_layer->tree; + l->popups = surface->data = wlr_scene_tree_create(layer_surface->current.layer + < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer); + l->scene->node.data = l->popups->node.data = l; + + wl_list_insert(&l->mon->layers[layer_surface->pending.layer],&l->link); + wlr_surface_send_enter(surface, layer_surface->output); + + /* Temporarily set the layer's current state to pending + * so that we can easily arrange it + */ + old_state = layer_surface->current; + layer_surface->current = layer_surface->pending; + l->mapped = 1; + arrangelayers(l->mon); + layer_surface->current = old_state; +} + +void //0.5 +createlocksurface(struct wl_listener *listener, void *data) +{ + SessionLock *lock = wl_container_of(listener, lock, new_surface); + struct wlr_session_lock_surface_v1 *lock_surface = data; + Monitor *m = lock_surface->output->data; + struct wlr_scene_tree *scene_tree = lock_surface->surface->data + = wlr_scene_subsurface_tree_create(lock->scene, lock_surface->surface); + m->lock_surface = lock_surface; + + wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y); + wlr_session_lock_surface_v1_configure(lock_surface, m->m.width, m->m.height); + + LISTEN(&lock_surface->events.destroy, &m->destroy_lock_surface, destroylocksurface); + + if (m == selmon) + client_notify_enter(lock_surface->surface, wlr_seat_get_keyboard(seat)); +} + +void //17 need fix 0.5 +createmon(struct wl_listener *listener, void *data) +{ + /* This event is raised by the backend when a new output (aka a display or + * monitor) becomes available. */ + struct wlr_output *wlr_output = data; + const MonitorRule *r; + size_t i; + Monitor *m = wlr_output->data = ecalloc(1, sizeof(*m)); + m->wlr_output = wlr_output; + + wl_list_init(&m->dwl_ipc_outputs); + wlr_output_init_render(wlr_output, alloc, drw); + + /* Initialize monitor state using configured rules */ + for (i = 0; i < LENGTH(m->layers); i++) + wl_list_init(&m->layers[i]); + + m->gappih = gappih; + m->gappiv = gappiv; + m->gappoh = gappoh; + m->gappov = gappov; + m->isoverview = 0; + m->sel = NULL; + m->is_in_hotarea = 0; + m->tagset[0] = m->tagset[1] = 1; + float scale = 1; + m->mfact = 0.5; + m->nmaster = 1; + enum wl_output_transform rr = WL_OUTPUT_TRANSFORM_NORMAL; + + if(LENGTH(layouts) > 1){ + m->lt[0] = &layouts[0]; //默认就有两个布局 + m->lt[1] = &layouts[1]; + }else{ + m->lt[0] = m->lt[1] = &layouts[0]; + } + for (r = monrules; r < END(monrules); r++) { + if (!r->name || strstr(wlr_output->name, r->name)) { + m->mfact = r->mfact; + m->nmaster = r->nmaster; + m->m.x = r->x; + m->m.y = r->y; + if(r->lt) + m->lt[0] = m->lt[1] = r->lt; + scale = r->scale; + rr = r->rr; + break; + } + } + + wlr_output_set_scale(wlr_output,scale); + wlr_xcursor_manager_load(cursor_mgr, scale); + wlr_output_set_transform(wlr_output, rr); + /* The mode is a tuple of (width, height, refresh rate), and each + * monitor supports only a specific set of modes. We just pick the + * monitor's preferred mode; a more sophisticated compositor would let + * the user configure it. */ + wlr_output_set_mode(wlr_output, wlr_output_preferred_mode(wlr_output)); + + /* Set up event listeners */ + LISTEN(&wlr_output->events.frame, &m->frame, rendermon); + LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon); + LISTEN(&wlr_output->events.request_state, &m->request_state, requestmonstate); + + wlr_output_enable(wlr_output, 1); + if (!wlr_output_commit(wlr_output)) + return; + + /* Try to enable adaptive sync, note that not all monitors support it. + * wlr_output_commit() will deactivate it in case it cannot be enabled */ + wlr_output_enable_adaptive_sync(wlr_output, 1); + wlr_output_commit(wlr_output); + + wl_list_insert(&mons, &m->link); + printstatus(); + + m->pertag = calloc(1, sizeof(Pertag)); + m->pertag->curtag = m->pertag->prevtag = 1; + + for (i = 0; i <= LENGTH(tags); i++) { + m->pertag->nmasters[i] = m->nmaster; + m->pertag->mfacts[i] = m->mfact; + + m->pertag->ltidxs[i][0] = m->lt[0]; + m->pertag->ltidxs[i][1] = m->lt[1]; + m->pertag->sellts[i] = m->sellt; + } + + + /* The xdg-protocol specifies: + * + * If the fullscreened surface is not opaque, the compositor must make + * sure that other screen content not part of the same surface tree (made + * up of subsurfaces, popups or similarly coupled surfaces) are not + * visible below the fullscreened surface. + * + */ + /* updatemons() will resize and set correct position */ + // m->fullscreen_bg = wlr_scene_rect_create(layers[LyrFS], 0, 0, fullscreen_bg); + // wlr_scene_node_set_enabled(&m->fullscreen_bg->node, 0); + + /* Adds this to the output layout in the order it was configured in. + * + * The output layout utility automatically adds a wl_output global to the + * display, which Wayland clients can see to find out information about the + * output (such as DPI, scale factor, manufacturer, etc). + */ + m->scene_output = wlr_scene_output_create(scene, wlr_output); + if (m->m.x < 0 || m->m.y < 0) + wlr_output_layout_add_auto(output_layout, wlr_output); + else + wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y); + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); +} + + + +void //fix for 0.5 +createnotify(struct wl_listener *listener, void *data) +{ + /* This event is raised when wlr_xdg_shell receives a new xdg surface from a + * client, either a toplevel (application window) or popup, + * or when wlr_layer_shell receives a new popup from a layer. + * If you want to do something tricky with popups you should check if + * its parent is wlr_xdg_shell or wlr_layer_shell */ + struct wlr_xdg_surface *xdg_surface = data; + Client *c = NULL; + LayerSurface *l = NULL; + + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + struct wlr_xdg_popup *popup = xdg_surface->popup; + struct wlr_box box; + if (toplevel_from_wlr_surface(popup->base->surface, &c, &l) < 0) + return; + popup->base->surface->data = wlr_scene_xdg_surface_create( + popup->parent->data, popup->base); + if ((l && !l->mon) || (c && !c->mon)) + return; + box = l ? l->mon->m : c->mon->w; + box.x -= (l ? l->geom.x : c->geom.x); + box.y -= (l ? l->geom.y : c->geom.y); + wlr_xdg_popup_unconstrain_from_box(popup, &box); + return; + } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE) + return; + + /* Allocate a Client for this surface */ + c = xdg_surface->data = ecalloc(1, sizeof(*c)); + c->surface.xdg = xdg_surface; + c->bw = borderpx; + + wlr_xdg_toplevel_set_wm_capabilities(xdg_surface->toplevel, + WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); + + LISTEN(&xdg_surface->surface->events.commit, &c->commit, commitnotify); + LISTEN(&xdg_surface->surface->events.map, &c->map, mapnotify); + LISTEN(&xdg_surface->surface->events.unmap, &c->unmap, unmapnotify); + LISTEN(&xdg_surface->events.destroy, &c->destroy, destroynotify); + LISTEN(&xdg_surface->toplevel->events.set_title, &c->set_title, updatetitle); + LISTEN(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen, + fullscreennotify); + LISTEN(&xdg_surface->toplevel->events.request_maximize, &c->maximize, + maximizenotify); + LISTEN(&xdg_surface->toplevel->events.request_minimize, &c->minimize, + minimizenotify); +} + +void // 0.5 +createpointer(struct wlr_pointer *pointer) +{ + struct libinput_device *device; + if (wlr_input_device_is_libinput(&pointer->base) + && (device = wlr_libinput_get_device_handle(&pointer->base))) { + + if (libinput_device_config_tap_get_finger_count(device)) { + libinput_device_config_tap_set_enabled(device, tap_to_click); + libinput_device_config_tap_set_drag_enabled(device, tap_and_drag); + libinput_device_config_tap_set_drag_lock_enabled(device, drag_lock); + libinput_device_config_tap_set_button_map(device, button_map); + } + + if (libinput_device_config_scroll_has_natural_scroll(device)) + libinput_device_config_scroll_set_natural_scroll_enabled(device, natural_scrolling); + + if (libinput_device_config_dwt_is_available(device)) + libinput_device_config_dwt_set_enabled(device, disable_while_typing); + + if (libinput_device_config_left_handed_is_available(device)) + libinput_device_config_left_handed_set(device, left_handed); + + if (libinput_device_config_middle_emulation_is_available(device)) + libinput_device_config_middle_emulation_set_enabled(device, middle_button_emulation); + + if (libinput_device_config_scroll_get_methods(device) != LIBINPUT_CONFIG_SCROLL_NO_SCROLL) + libinput_device_config_scroll_set_method (device, scroll_method); + + if (libinput_device_config_click_get_methods(device) != LIBINPUT_CONFIG_CLICK_METHOD_NONE) + libinput_device_config_click_set_method (device, click_method); + + if (libinput_device_config_send_events_get_modes(device)) + libinput_device_config_send_events_set_mode(device, send_events_mode); + + if (libinput_device_config_accel_is_available(device)) { + libinput_device_config_accel_set_profile(device, accel_profile); + libinput_device_config_accel_set_speed(device, accel_speed); + } + } + + wlr_cursor_attach_input_device(cursor, &pointer->base); +} + +void // 0.5 +createpointerconstraint(struct wl_listener *listener, void *data) +{ + PointerConstraint *pointer_constraint = ecalloc(1, sizeof(*pointer_constraint)); + pointer_constraint->constraint = data; + LISTEN(&pointer_constraint->constraint->events.destroy, + &pointer_constraint->destroy, destroypointerconstraint); +} + +void // 0.5 +cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) +{ + if (active_constraint == constraint) + return; + + if (active_constraint) + wlr_pointer_constraint_v1_send_deactivated(active_constraint); + + active_constraint = constraint; + wlr_pointer_constraint_v1_send_activated(constraint); +} + + +void //0.5 +cursorframe(struct wl_listener *listener, void *data) +{ + /* This event is forwarded by the cursor when a pointer emits an frame + * event. Frame events are sent after regular pointer events to group + * multiple events together. For instance, two axis events may happen at the + * same time, in which case a frame event won't be sent in between. */ + /* Notify the client with pointer focus of the frame event. */ + wlr_seat_pointer_notify_frame(seat); +} + +void // 0.5 +cursorwarptohint(void) +{ + Client *c = NULL; + double sx = active_constraint->current.cursor_hint.x; + double sy = active_constraint->current.cursor_hint.y; + + toplevel_from_wlr_surface(active_constraint->surface, &c, NULL); + /* TODO: wlroots 0.18: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4478 */ + if (c && (active_constraint->current.committed & WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT )) { + wlr_cursor_warp(cursor, NULL, sx + c->geom.x + c->bw, sy + c->geom.y + c->bw); + wlr_seat_pointer_warp(active_constraint->seat, sx, sy); + } +} + +void +defaultgaps(const Arg *arg) +{ + setgaps(gappoh, gappov, gappih, gappiv); +} + + + +void //0.5 +destroydragicon(struct wl_listener *listener, void *data) +{ + /* Focus enter isn't sent during drag, so refocus the focused node. */ + focusclient(focustop(selmon), 1); + motionnotify(0, NULL, 0, 0, 0, 0); +} + +void //17 +destroyidleinhibitor(struct wl_listener *listener, void *data) +{ + /* `data` is the wlr_surface of the idle inhibitor being destroyed, + * at this point the idle inhibitor is still in the list of the manager */ + checkidleinhibitor(wlr_surface_get_root_surface(data)); +} + +void //0.5 +destroylayersurfacenotify(struct wl_listener *listener, void *data) +{ + LayerSurface *l = wl_container_of(listener, l, destroy); + + wl_list_remove(&l->link); + wl_list_remove(&l->destroy.link); + wl_list_remove(&l->map.link); + wl_list_remove(&l->unmap.link); + wl_list_remove(&l->surface_commit.link); + wlr_scene_node_destroy(&l->scene->node); + wlr_scene_node_destroy(&l->popups->node); + free(l); +} + +void //0.5 +destroylock(SessionLock *lock, int unlock) +{ + wlr_seat_keyboard_notify_clear_focus(seat); + if ((locked = !unlock)) + goto destroy; + + wlr_scene_node_set_enabled(&locked_bg->node, 0); + + focusclient(focustop(selmon), 0); + motionnotify(0, NULL, 0, 0, 0, 0); + +destroy: + wl_list_remove(&lock->new_surface.link); + wl_list_remove(&lock->unlock.link); + wl_list_remove(&lock->destroy.link); + + wlr_scene_node_destroy(&lock->scene->node); + cur_lock = NULL; + free(lock); +} + +void //0.5 +destroylocksurface(struct wl_listener *listener, void *data) +{ + Monitor *m = wl_container_of(listener, m, destroy_lock_surface); + struct wlr_session_lock_surface_v1 *surface, *lock_surface = m->lock_surface; + + m->lock_surface = NULL; + wl_list_remove(&m->destroy_lock_surface.link); + + if (lock_surface->surface != seat->keyboard_state.focused_surface) + return; + + if (locked && cur_lock && !wl_list_empty(&cur_lock->surfaces)) { + surface = wl_container_of(cur_lock->surfaces.next, surface, link); + client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat)); + } else if (!locked) { + focusclient(focustop(selmon), 1); + } else { + wlr_seat_keyboard_clear_focus(seat); + } +} + + +void //0.5 +destroynotify(struct wl_listener *listener, void *data) +{ + /* Called when the xdg_toplevel is destroyed. */ + Client *c = wl_container_of(listener, c, destroy); + wl_list_remove(&c->destroy.link); + wl_list_remove(&c->set_title.link); + wl_list_remove(&c->fullscreen.link); + wl_list_remove(&c->maximize.link); + wl_list_remove(&c->minimize.link); +#ifdef XWAYLAND + if (c->type != XDGShell) { + wl_list_remove(&c->activate.link); + wl_list_remove(&c->associate.link); + wl_list_remove(&c->configure.link); + wl_list_remove(&c->dissociate.link); + wl_list_remove(&c->set_hints.link); + } else +#endif + { + wl_list_remove(&c->commit.link); + wl_list_remove(&c->map.link); + wl_list_remove(&c->unmap.link); + } + free(c); +} + +void // 0.5 +destroypointerconstraint(struct wl_listener *listener, void *data) +{ + PointerConstraint *pointer_constraint = wl_container_of(listener, pointer_constraint, destroy); + + if (active_constraint == pointer_constraint->constraint) { + cursorwarptohint(); + active_constraint = NULL; + } + + wl_list_remove(&pointer_constraint->destroy.link); + free(pointer_constraint); +} + +void //0.5 +destroysessionlock(struct wl_listener *listener, void *data) +{ + SessionLock *lock = wl_container_of(listener, lock, destroy); + destroylock(lock, 0); + motionnotify(0, NULL, 0, 0, 0, 0); +} + + +void //0.5 +destroysessionmgr(struct wl_listener *listener, void *data) +{ + wl_list_remove(&lock_listener.link); + wl_list_remove(&listener->link); +} + +void //17 +associatex11(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, associate); + + LISTEN(&client_surface(c)->events.map, &c->map, mapnotify); + LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmapnotify); +} + +void //17 +dissociatex11(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, dissociate); + wl_list_remove(&c->map.link); + wl_list_remove(&c->unmap.link); +} + + +Monitor * //0.5 +dirtomon(enum wlr_direction dir) +{ + struct wlr_output *next; + if (!wlr_output_layout_get(output_layout, selmon->wlr_output)) + return selmon; + if ((next = wlr_output_layout_adjacent_output(output_layout, + dir, selmon->wlr_output, selmon->m.x, selmon->m.y))) + return next->data; + if ((next = wlr_output_layout_farthest_output(output_layout, + dir ^ (WLR_DIRECTION_LEFT|WLR_DIRECTION_RIGHT), + selmon->wlr_output, selmon->m.x, selmon->m.y))) + return next->data; + return selmon; +} + + + +void dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { + struct wl_resource *manager_resource = wl_resource_create(client, &zdwl_ipc_manager_v2_interface, version, id); + if (!manager_resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(manager_resource, &dwl_manager_implementation, NULL, dwl_ipc_manager_destroy); + + zdwl_ipc_manager_v2_send_tags(manager_resource, LENGTH(tags)); + + for (int i = 0; i < LENGTH(layouts); i++) + zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol); +} + +void dwl_ipc_manager_destroy(struct wl_resource *resource) { + /* No state to destroy */ +} + +void dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output) { + DwlIpcOutput *ipc_output; + Monitor *monitor = wlr_output_from_resource(output)->data; + struct wl_resource *output_resource = wl_resource_create(client, &zdwl_ipc_output_v2_interface, wl_resource_get_version(resource), id); + if (!output_resource) + return; + + ipc_output = ecalloc(1, sizeof(*ipc_output)); + ipc_output->resource = output_resource; + ipc_output->monitor = monitor; + wl_resource_set_implementation(output_resource, &dwl_output_implementation, ipc_output, dwl_ipc_output_destroy); + wl_list_insert(&monitor->dwl_ipc_outputs, &ipc_output->link); + dwl_ipc_output_printstatus_to(ipc_output); +} + +void dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +// 在外部ipc客户端结束的时候会发出销毁请求,比如kill掉waybar,就会销毁waybar绑定的ipc_output +static void dwl_ipc_output_destroy(struct wl_resource *resource) { + DwlIpcOutput *ipc_output = wl_resource_get_user_data(resource); + wl_list_remove(&ipc_output->link); + free(ipc_output); +} + +void dwl_ipc_output_printstatus(Monitor *monitor) { + DwlIpcOutput *ipc_output; + wl_list_for_each(ipc_output, &monitor->dwl_ipc_outputs, link) + dwl_ipc_output_printstatus_to(ipc_output); +} + +void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) { + Monitor *monitor = ipc_output->monitor; + Client *c, *focused; + int tagmask, state, numclients, focused_client, tag; + const char *title, *appid,*symbol; + focused = focustop(monitor); + zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon); + + if ((monitor->tagset[monitor->seltags] & TAGMASK) == TAGMASK) { + state = 0; + state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE; + zdwl_ipc_output_v2_send_tag(ipc_output->resource, 888, state, 1, 1); + } else { + for ( tag = 0 ; tag < LENGTH(tags); tag++) { + numclients = state = focused_client = 0; + tagmask = 1 << tag; + if ((tagmask & monitor->tagset[monitor->seltags]) != 0) + state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE; + + wl_list_for_each(c, &clients, link) { + if (c->mon != monitor) + continue; + if (!(c->tags & tagmask)) + continue; + if (c == focused) + focused_client = 1; + if (c->isurgent) + state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT; + + numclients++; + } + zdwl_ipc_output_v2_send_tag(ipc_output->resource, tag, state, numclients, focused_client); + } + } + + + title = focused ? client_get_title(focused) : ""; + appid = focused ? client_get_appid(focused) : ""; + symbol = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]->symbol; + + zdwl_ipc_output_v2_send_layout(ipc_output->resource, monitor->lt[monitor->sellt] - layouts); + zdwl_ipc_output_v2_send_title(ipc_output->resource, title ? title : broken); + zdwl_ipc_output_v2_send_appid(ipc_output->resource, appid ? appid : broken); + zdwl_ipc_output_v2_send_layout_symbol(ipc_output->resource, symbol); + zdwl_ipc_output_v2_send_frame(ipc_output->resource); +} + +void dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags) { + DwlIpcOutput *ipc_output; + Monitor *monitor; + Client *selected_client; + unsigned int newtags = 0; + + ipc_output = wl_resource_get_user_data(resource); + if (!ipc_output) + return; + + monitor = ipc_output->monitor; + selected_client = focustop(monitor); + if (!selected_client) + return; + + newtags = (selected_client->tags & and_tags) ^ xor_tags; + if (!newtags) + return; + + selected_client->tags = newtags; + focusclient(focustop(selmon), 1); + arrange(selmon); + printstatus(); +} + +void dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index) { + DwlIpcOutput *ipc_output; + Monitor *monitor; + + ipc_output = wl_resource_get_user_data(resource); + if (!ipc_output) + return; + + monitor = ipc_output->monitor; + if (index >= LENGTH(layouts)) + return; + if (index != monitor->lt[monitor->sellt] - layouts) + monitor->sellt ^= 1; + + monitor->lt[monitor->sellt] = &layouts[index]; + arrange(monitor); + printstatus(); +} + +void dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset) { + DwlIpcOutput *ipc_output; + Monitor *monitor; + unsigned int newtags = tagmask & TAGMASK; + + ipc_output = wl_resource_get_user_data(resource); + if (!ipc_output) + return; + monitor = ipc_output->monitor; + + if (!newtags || newtags == monitor->tagset[monitor->seltags]) + return; + if (toggle_tagset) + monitor->seltags ^= 1; + + monitor->tagset[monitor->seltags] = newtags; + focusclient(focustop(monitor), 1); + arrange(monitor); + printstatus(); +} + +void dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource) { + wl_resource_destroy(resource); +} + + +void +focusclient(Client *c, int lift) +{ + struct wlr_surface *old = seat->keyboard_state.focused_surface; + if (locked) + return; + + if (c && c->iskilling) + return; + + /* Raise client in stacking order if requested */ + if (c && lift) + wlr_scene_node_raise_to_top(&c->scene->node); //将视图提升到顶层 + + if (c && client_surface(c) == old) + return; + + if (c && c->mon && c->mon != selmon) { + selmon = c->mon; + } + + if(selmon && selmon->sel && selmon->sel->foreign_toplevel) { + wlr_foreign_toplevel_handle_v1_set_activated(selmon->sel->foreign_toplevel,false); + } + if(selmon) + selmon->sel = c; + if(c && c->foreign_toplevel) + wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel,true); + + /* Put the new client atop the focus stack and select its monitor */ + if (c && !client_is_unmanaged(c)) { + wl_list_remove(&c->flink); + wl_list_insert(&fstack, &c->flink); + selmon = c->mon; + c->isurgent = 0; + client_restack_surface(c); + setborder_color(c); + /* Don't change border color if there is an exclusive focus or we are + * handling a drag operation */ + } + + /* Deactivate old client if focus is changing */ + if (old && (!c || client_surface(c) != old)) { + /* If an overlay is focused, don't focus or activate the client, + * but only update its position in fstack to render its border with focuscolor + * and focus it after the overlay is closed. */ + Client *w = NULL; + LayerSurface *l = NULL; + int type = toplevel_from_wlr_surface(old, &w, &l); + if (type == LayerShell && l->scene->node.enabled + && l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { + return; + } else if (w && w == exclusive_focus && client_wants_focus(w)) { + return; + /* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg + * and probably other clients */ + } else if (w && !client_is_unmanaged(w) && (!c || !client_wants_focus(c))) { + setborder_color(w); + + client_activate_surface(old, 0); + } + } + printstatus(); + + if (!c) { + /* With no client, all we have left is to clear focus */ + if(selmon && selmon->sel) + selmon->sel = NULL; //这个很关键,因为很多地方用到当前窗口做计算,不重置成NULL就会到处有野指针 + #ifdef IM + dwl_input_method_relay_set_focus(input_relay, NULL); + #endif + wlr_seat_keyboard_notify_clear_focus(seat); + return; + } + + /* Change cursor surface */ + motionnotify(0, NULL, 0, 0, 0, 0); + + /* Have a client, so focus its top-level wlr_surface */ + client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat)); + +#ifdef IM + struct wlr_keyboard *keyboard; + keyboard = wlr_seat_get_keyboard(seat); + uint32_t mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + if (mods == 0) + dwl_input_method_relay_set_focus(input_relay, client_surface(c)); +#endif + /* Activate the new client */ + client_activate_surface(client_surface(c), 1); +} + +void // 0.5 +focusmon(const Arg *arg) +{ + int i = 0, nmons = wl_list_length(&mons); + if (nmons) { + do /* don't switch to disabled mons */ + selmon = dirtomon(arg->i); + while (!selmon->wlr_output->enabled && i++ < nmons); + if (!selmon->wlr_output->enabled) + selmon = NULL; + } + warp_cursor_to_selmon(selmon); + focusclient(focustop(selmon), 1); +} + +void //17 +focusstack(const Arg *arg) +{ + /* Focus the next or previous client (in tiling order) on selmon */ + Client *c, *sel = focustop(selmon); + if (!sel || sel->isfullscreen) + return; + if (arg->i > 0) { + wl_list_for_each(c, &sel->link, link) { + if (&c->link == &clients) + continue; /* wrap past the sentinel node */ + if (VISIBLEON(c, selmon)) + break; /* found it */ + } + } else { + wl_list_for_each_reverse(c, &sel->link, link) { + if (&c->link == &clients) + continue; /* wrap past the sentinel node */ + if (VISIBLEON(c, selmon)) + break; /* found it */ + } + } + /* If only one client is visible on selmon, then c == sel */ + focusclient(c, 1); +} + +/* We probably should change the name of this, it sounds like + * will focus the topmost client of this mon, when actually will + * only return that client */ +Client * //0.5 +focustop(Monitor *m) +{ + Client *c; + wl_list_for_each(c, &fstack, flink) { + if (VISIBLEON(c, m)) + return c; + } + return NULL; +} + +void // 0.6 +fullscreennotify(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, fullscreen); + setfullscreen(c, client_wants_fullscreen(c)); +} + +void +incnmaster(const Arg *arg) +{ + if (!arg || !selmon) + return; + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); +} + +void +incgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +void +incigaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +void +incihgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + ); +} + +void +incivgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih, + selmon->gappiv + arg->i + ); +} + +void +incogaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); +} + +void +incohgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov, + selmon->gappih, + selmon->gappiv + ); +} + +void +requestmonstate(struct wl_listener *listener, void *data) +{ + struct wlr_output_event_request_state *event = data; + wlr_output_commit_state(event->output, event->state); + updatemons(NULL, NULL); +} + + +void +incovgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); +} + +void //17 +inputdevice(struct wl_listener *listener, void *data) +{ + /* This event is raised by the backend when a new input device becomes + * available. */ + struct wlr_input_device *device = data; + uint32_t caps; + + switch (device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + createkeyboard(wlr_keyboard_from_input_device(device)); + break; + case WLR_INPUT_DEVICE_POINTER: + createpointer(wlr_pointer_from_input_device(device)); + break; + default: + /* TODO handle other input device types */ + break; + } + + /* We need to let the wlr_seat know what our capabilities are, which is + * communiciated to the client. In dwl we always have a cursor, even if + * there are no pointer devices, so we always include that capability. */ + /* TODO do we actually require a cursor? */ + caps = WL_SEAT_CAPABILITY_POINTER; + if (!wl_list_empty(&keyboards)) + caps |= WL_SEAT_CAPABILITY_KEYBOARD; + wlr_seat_set_capabilities(seat, caps); +} + +int +keyrepeat(void *data) +{ + Keyboard *kb = data; + int i; + if (!kb->nsyms || kb->wlr_keyboard->repeat_info.rate <= 0) + return 0; + + wl_event_source_timer_update(kb->key_repeat_source, + 1000 / kb->wlr_keyboard->repeat_info.rate); + + for (i = 0; i < kb->nsyms; i++) + keybinding(kb->mods, kb->keysyms[i]); + + return 0; +} + + +int //17 +keybinding(uint32_t mods, xkb_keysym_t sym) +{ + /* + * Here we handle compositor keybindings. This is when the compositor is + * processing keys, rather than passing them on to the client for its own + * processing. + */ + int handled = 0; + const Key *k; + for (k = keys; k < END(keys); k++) { + if (CLEANMASK(mods) == CLEANMASK(k->mod) && + sym == k->keysym && k->func) { + k->func(&k->arg); + handled = 1; + } + } + return handled; +} + +void //17 +keypress(struct wl_listener *listener, void *data) +{ + int i; + /* This event is raised when a key is pressed or released. */ + Keyboard *kb = wl_container_of(listener, kb, key); + struct wlr_keyboard_key_event *event = data; + + /* Translate libinput keycode -> xkbcommon */ + uint32_t keycode = event->keycode + 8; + /* Get a list of keysyms based on the keymap for this keyboard */ + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms( + kb->wlr_keyboard->xkb_state, keycode, &syms); + + int handled = 0; + uint32_t mods = wlr_keyboard_get_modifiers(kb->wlr_keyboard); + + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); +#ifdef IM + if (!locked && event->state == WL_KEYBOARD_KEY_STATE_RELEASED && (keycode == 133 ||keycode == 37 || keycode == 64 || keycode == 50 || keycode == 134 || keycode == 105 || keycode == 108 || keycode == 62) && selmon->sel) { + dwl_input_method_relay_set_focus(input_relay, client_surface(selmon->sel)); + } +#endif + + /* On _press_ if there is no active screen locker, + * attempt to process a compositor keybinding. */ + if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) + for (i = 0; i < nsyms; i++) + handled = keybinding(mods, syms[i]) || handled; + + if (handled && kb->wlr_keyboard->repeat_info.delay > 0) { + kb->mods = mods; + kb->keysyms = syms; + kb->nsyms = nsyms; + wl_event_source_timer_update(kb->key_repeat_source, + kb->wlr_keyboard->repeat_info.delay); + } else { + kb->nsyms = 0; + wl_event_source_timer_update(kb->key_repeat_source, 0); + } + + if (handled) + return; + + +#ifdef IM + /* if there is a keyboard grab, we send the key there */ + struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(kb); + if (kb_grab) { + wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab,kb->wlr_keyboard); + wlr_input_method_keyboard_grab_v2_send_key(kb_grab,event->time_msec, event->keycode, event->state); + wlr_log(WLR_DEBUG, "keypress send to IM:%u mods %u state %u",event->keycode, mods,event->state); + return; + } +#endif + + /* Pass unhandled keycodes along to the client. */ + wlr_seat_set_keyboard(seat, kb->wlr_keyboard); + wlr_seat_keyboard_notify_key(seat, event->time_msec, + event->keycode, event->state); +} + + +void //17 +keypressmod(struct wl_listener *listener, void *data) +{ + /* This event is raised when a modifier key, such as shift or alt, is + * pressed. We simply communicate this to the client. */ + Keyboard *kb = wl_container_of(listener, kb, modifiers); +#ifdef IM + struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(kb); + if (kb_grab) { + wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, + &kb->wlr_keyboard->modifiers); + wlr_log(WLR_DEBUG, "keypressmod send to IM"); + return; + } +#endif + /* + * A seat can only have one keyboard, but this is a limitation of the + * Wayland protocol - not wlroots. We assign all connected keyboards to the + * same seat. You can swap out the underlying wlr_keyboard like this and + * wlr_seat handles this transparently. + */ + wlr_seat_set_keyboard(seat, kb->wlr_keyboard); + /* Send modifiers to the client. */ + wlr_seat_keyboard_notify_modifiers(seat, + &kb->wlr_keyboard->modifiers); +} + + +void +killclient(const Arg *arg) +{ + Client *c; + c = selmon->sel; + if (c) { + c->iskilling = 1; + selmon->sel = NULL; + client_send_close(c); + } + +} + +void //0.5 +locksession(struct wl_listener *listener, void *data) +{ + struct wlr_session_lock_v1 *session_lock = data; + SessionLock *lock; + wlr_scene_node_set_enabled(&locked_bg->node, 1); + if (cur_lock) { + wlr_session_lock_v1_destroy(session_lock); + return; + } + lock = session_lock->data = ecalloc(1, sizeof(*lock)); + focusclient(NULL, 0); + + lock->scene = wlr_scene_tree_create(layers[LyrBlock]); + cur_lock = lock->lock = session_lock; + locked = 1; + + LISTEN(&session_lock->events.new_surface, &lock->new_surface, createlocksurface); + LISTEN(&session_lock->events.destroy, &lock->destroy, destroysessionlock); + LISTEN(&session_lock->events.unlock, &lock->unlock, unlocksession); + + wlr_session_lock_v1_send_locked(session_lock); +} + +void //0.5 +maplayersurfacenotify(struct wl_listener *listener, void *data) +{ + motionnotify(0, NULL, 0, 0, 0, 0); +} + +void //old fix to 0.5 +mapnotify(struct wl_listener *listener, void *data) +{ + /* Called when the surface is mapped, or ready to display on-screen. */ + Client *p = NULL; + Client *c = wl_container_of(listener, c, map); + int i; + /* Create scene tree for this client and its border */ + c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]); + wlr_scene_node_set_enabled(&c->scene->node, c->type != XDGShell); + c->scene_surface = c->type == XDGShell + ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg) + : wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); + c->scene->node.data = c->scene_surface->node.data = c; + + client_get_geometry(c, &c->geom); + + /* Handle unmanaged clients first so we can return prior create borders */ + if (client_is_unmanaged(c)) { + /* Unmanaged clients always are floating */ + wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]); + wlr_scene_node_set_position(&c->scene->node, c->geom.x + borderpx, + c->geom.y + borderpx); + if (client_wants_focus(c)) { + focusclient(c, 1); + exclusive_focus = c; + } + return; + } + + for (i = 0; i < 4; i++) { + c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, + c->isurgent ? urgentcolor : bordercolor); + c->border[i]->node.data = c; + } + + /* Initialize client geometry with room for border */ + client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); + c->geom.width += 2 * c->bw; + c->geom.height += 2 * c->bw; + c->isfakefullscreen = 0; + c->isfullscreen = 0; + c->istiled = 0; + c->ignore_clear_fullscreen = 0; + c->iskilling = 0; + // c->animainit_geom.width = c->geom.width * zoom_initial_ratio; + // c->animainit_geom.height = c->geom.height * zoom_initial_ratio; + // c->animainit_geom.x = c->geom.x + (c->geom.width - c->animainit_geom.width)/2; + // c->animainit_geom.y = c->geom.y + (c->geom.height - c->animainit_geom.height)/2; + c->is_first_resize = true; + //nop + if (new_is_master) + // tile at the top + wl_list_insert(&clients, &c->link); //新窗口是master,头部入栈 + else + wl_list_insert(clients.prev, &c->link); //尾部入栈 + wl_list_insert(&fstack, &c->flink); + + /* Set initial monitor, tags, floating status, and focus: + * we always consider floating, clients that have parent and thus + * we set the same tags and monitor than its parent, if not + * try to apply rules for them */ + if ((p = client_get_parent(c))) { + c->isfloating = 1; + setmon(c, p->mon, p->tags); + } else { + applyrules(c); + } + + //创建外部顶层窗口的句柄,每一个顶层窗口都有一个 + c->foreign_toplevel = wlr_foreign_toplevel_handle_v1_create(foreign_toplevel_manager); + + //监听来自外部对于窗口的事件请求 + if(c->foreign_toplevel){ + LISTEN(&(c->foreign_toplevel->events.request_activate), + &c->foreign_activate_request,handle_foreign_activate_request); + LISTEN(&(c->foreign_toplevel->events.request_fullscreen), + &c->foreign_fullscreen_request,handle_foreign_fullscreen_request); + LISTEN(&(c->foreign_toplevel->events.request_close), + &c->foreign_close_request,handle_foreign_close_request); + LISTEN(&(c->foreign_toplevel->events.destroy), + &c->foreign_destroy,handle_foreign_destroy); + //设置外部顶层句柄的id为应用的id + const char *appid; + appid = client_get_appid(c) ; + if(appid) + wlr_foreign_toplevel_handle_v1_set_app_id(c->foreign_toplevel,appid); + //设置外部顶层句柄的title为应用的title + const char *title; + title = client_get_title(c) ; + if(title) + wlr_foreign_toplevel_handle_v1_set_title(c->foreign_toplevel,title); + //设置外部顶层句柄的显示监视器为当前监视器 + wlr_foreign_toplevel_handle_v1_output_enter( + c->foreign_toplevel, selmon->wlr_output); + } + + if(selmon->sel && selmon->sel->foreign_toplevel) + wlr_foreign_toplevel_handle_v1_set_activated(selmon->sel->foreign_toplevel,false); + selmon->sel = c; + if(c->foreign_toplevel) + wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel,true); + + printstatus(); + +} + +void //0.5 +maximizenotify(struct wl_listener *listener, void *data) +{ + /* This event is raised when a client would like to maximize itself, + * typically because the user clicked on the maximize button on + * client-side decorations. dwl doesn't support maximization, but + * to conform to xdg-shell protocol we still must send a configure. + * Since xdg-shell protocol v5 we should ignore request of unsupported + * capabilities, just schedule a empty configure when the client uses <5 + * protocol version + * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ + // Client *c = wl_container_of(listener, c, maximize); + // if (wl_resource_get_version(c->surface.xdg->toplevel->resource) + // < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) + // wlr_xdg_surface_schedule_configure(c->surface.xdg); + togglefakefullscreen(&(Arg){0}); +} + +void set_minized(Client *c) { + if (c->isglobal) { + c->isglobal = 0; + selmon->sel->tags = selmon->tagset[selmon->seltags]; + } + c->is_scratchpad_show = 0; + c->oldtags = c->mon->sel->tags; + c->tags = 0; + c->isminied = 1; + c->is_in_scratchpad = 1; + c->is_scratchpad_show = 0; + focusclient(focustop(selmon), 1); + arrange(c->mon); + wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel,false); + wlr_foreign_toplevel_handle_v1_set_minimized(c->foreign_toplevel,true); + wl_list_remove(&c->link); //从原来位置移除 + wl_list_insert(clients.prev, &c->link); //插入尾部 + +} + +void //0.5 +minimizenotify(struct wl_listener *listener, void *data) +{ + /* This event is raised when a client would like to maximize itself, + * typically because the user clicked on the maximize button on + * client-side decorations. dwl doesn't support maximization, but + * to conform to xdg-shell protocol we still must send a configure. + * Since xdg-shell protocol v5 we should ignore request of unsupported + * capabilities, just schedule a empty configure when the client uses <5 + * protocol version + * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ + // Client *c = wl_container_of(listener, c, maximize); + // if (wl_resource_get_version(c->surface.xdg->toplevel->resource) + // < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) + // wlr_xdg_surface_schedule_configure(c->surface.xdg); + // togglefakefullscreen(&(Arg){0}); + if(selmon->sel) { + set_minized(selmon->sel); + } +} + + + +void //17 +monocle(Monitor *m) +{ + Client *c; + int n = 0; + + wl_list_for_each(c, &clients, link) { + if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) + continue; + resize(c, m->w, 0); + n++; + } + if (n) + snprintf(m->ltsymbol, LENGTH(m->ltsymbol), "[%d]", n); + if ((c = focustop(m))) + wlr_scene_node_raise_to_top(&c->scene->node); +} + +void //0.5 +motionabsolute(struct wl_listener *listener, void *data) +{ + /* This event is forwarded by the cursor when a pointer emits an _absolute_ + * motion event, from 0..1 on each axis. This happens, for example, when + * wlroots is running under a Wayland window rather than KMS+DRM, and you + * move the mouse over the window. You could enter the window from any edge, + * so we have to warp the mouse there. There is also some hardware which + * emits these events. */ + struct wlr_pointer_motion_absolute_event *event = data; + double lx, ly, dx, dy; + + if (!event->time_msec) /* this is 0 with virtual pointers */ + wlr_cursor_warp_absolute(cursor, &event->pointer->base, event->x, event->y); + + wlr_cursor_absolute_to_layout_coords(cursor, &event->pointer->base, event->x, event->y, &lx, &ly); + dx = lx - cursor->x; + dy = ly - cursor->y; + motionnotify(event->time_msec, &event->pointer->base, dx, dy, dx, dy); +} + +void //fix for 0.5 +motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, + double dx_unaccel, double dy_unaccel) +{ + double sx = 0, sy = 0, sx_confined, sy_confined; + Client *c = NULL, *w = NULL; + LayerSurface *l = NULL; + struct wlr_surface *surface = NULL; + struct wlr_pointer_constraint_v1 *constraint; + + /* Find the client under the pointer and send the event along. */ + xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy); + + if (cursor_mode == CurPressed && !seat->drag + && surface != seat->pointer_state.focused_surface + && toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, &l) >= 0) { + c = w; + surface = seat->pointer_state.focused_surface; + sx = cursor->x - (l ? l->geom.x : w->geom.x); + sy = cursor->y - (l ? l->geom.y : w->geom.y); + } + + /* time is 0 in internal calls meant to restore pointer focus. */ + if (time) { + wlr_relative_pointer_manager_v1_send_relative_motion( + relative_pointer_mgr, seat, (uint64_t)time * 1000, + dx, dy, dx_unaccel, dy_unaccel); + + wl_list_for_each(constraint, &pointer_constraints->constraints, link) + cursorconstrain(constraint); + + if (active_constraint && cursor_mode != CurResize && cursor_mode != CurMove) { + toplevel_from_wlr_surface(active_constraint->surface, &c, NULL); + if (c && active_constraint->surface == seat->pointer_state.focused_surface) { + sx = cursor->x - c->geom.x - c->bw; + sy = cursor->y - c->geom.y - c->bw; + if (wlr_region_confine(&active_constraint->region, sx, sy, + sx + dx, sy + dy, &sx_confined, &sy_confined)) { + dx = sx_confined - sx; + dy = sy_confined - sy; + } + + if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) + return; + } + } + + wlr_cursor_move(cursor, device, dx, dy); + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + + /* Update selmon (even while dragging a window) */ + if (sloppyfocus) + selmon = xytomon(cursor->x, cursor->y); + } + + /* Update drag icon's position */ + wlr_scene_node_set_position(&drag_icon->node, cursor->x, cursor->y); + + /* If we are currently grabbing the mouse, handle and return */ + if (cursor_mode == CurMove) { + /* Move the grabbed client to the new position. */ + grabc->oldgeom = (struct wlr_box){.x = cursor->x - grabcx, .y = cursor->y - grabcy, + .width = grabc->geom.width, .height = grabc->geom.height}; + resize(grabc, grabc->oldgeom, 1); + return; + } else if (cursor_mode == CurResize) { + grabc->oldgeom = (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y, + .width = cursor->x - grabc->geom.x, .height = cursor->y - grabc->geom.y}; + resize(grabc, grabc->oldgeom, 1); + return; + } + + /* If there's no client surface under the cursor, set the cursor image to a + * default. This is what makes the cursor image appear when you move it + * off of a client or over its border. */ + if (!surface && !seat->drag) + wlr_cursor_set_xcursor(cursor, cursor_mgr, "left_ptr"); + + pointerfocus(c, surface, sx, sy, time); +} + + +void // fix for 0.5 光标相对位置移动事件处理 +motionrelative(struct wl_listener *listener, void *data) +{ + /* This event is forwarded by the cursor when a pointer emits a _relative_ + * pointer motion event (i.e. a delta) */ + struct wlr_pointer_motion_event *event = data; + /* The cursor doesn't move unless we tell it to. The cursor automatically + * handles constraining the motion to the output layout, as well as any + * special configuration applied for the specific input device which + * generated the event. You can pass NULL for the device if you want to move + * the cursor around without any input. */ + + + // //处理一些事件,比如窗口聚焦,图层聚焦通知到客户端 + // motionnotify(event->time_msec); + // //扩展事件通知,没有这个鼠标移动的时候滑轮将无法使用 + // wlr_relative_pointer_manager_v1_send_relative_motion( + // pointer_manager, + // seat, (uint64_t)(event->time_msec) * 1000, + // event->delta_x, event->delta_y, + // event->unaccel_dx, event->unaccel_dy); + // //通知光标设备移动 + // wlr_cursor_move(cursor, &event->pointer->base, event->delta_x, event->delta_y); + motionnotify(event->time_msec, &event->pointer->base, event->delta_x, event->delta_y, + event->unaccel_dx, event->unaccel_dy); + //鼠标左下热区判断是否触发 + toggle_hotarea(cursor->x,cursor->y); +} + +void //17 +moveresize(const Arg *arg) +{ + if (cursor_mode != CurNormal && cursor_mode != CurPressed) + return; + xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL); + if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen) + return; + + /* Float the window and tell motionnotify to grab it */ + if(grabc->isfloating == 0) { + setfloating(grabc, 1); + } + + switch (cursor_mode = arg->ui) { + case CurMove: + grabcx = cursor->x - grabc->geom.x; + grabcy = cursor->y - grabc->geom.y; + wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur"); + break; + case CurResize: + /* Doesn't work for X11 output - the next absolute motion event + * returns the cursor to where it started */ + wlr_cursor_warp_closest(cursor, NULL, + grabc->geom.x + grabc->geom.width, + grabc->geom.y + grabc->geom.height); + wlr_cursor_set_xcursor(cursor, cursor_mgr, "bottom_right_corner"); + break; + } +} + +void //0.5 +outputmgrapply(struct wl_listener *listener, void *data) +{ + struct wlr_output_configuration_v1 *config = data; + outputmgrapplyortest(config, 0); +} + +void //0.5 +outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) +{ + /* + * Called when a client such as wlr-randr requests a change in output + * configuration. This is only one way that the layout can be changed, + * so any Monitor information should be updated by updatemons() after an + * output_layout.change event, not here. + */ + struct wlr_output_configuration_head_v1 *config_head; + int ok = 1; + + wl_list_for_each(config_head, &config->heads, link) { + struct wlr_output *wlr_output = config_head->state.output; + Monitor *m = wlr_output->data; + struct wlr_output_state state; + + wlr_output_state_init(&state); + wlr_output_state_set_enabled(&state, config_head->state.enabled); + if (!config_head->state.enabled) + goto apply_or_test; + + if (config_head->state.mode) + wlr_output_state_set_mode(&state, config_head->state.mode); + else + wlr_output_state_set_custom_mode(&state, + config_head->state.custom_mode.width, + config_head->state.custom_mode.height, + config_head->state.custom_mode.refresh); + + wlr_output_state_set_transform(&state, config_head->state.transform); + wlr_output_state_set_scale(&state, config_head->state.scale); + wlr_output_state_set_adaptive_sync_enabled(&state, + config_head->state.adaptive_sync_enabled); + +apply_or_test: + ok &= test ? wlr_output_test_state(wlr_output, &state) + : wlr_output_commit_state(wlr_output, &state); + + /* Don't move monitors if position wouldn't change, this to avoid + * wlroots marking the output as manually configured. + * wlr_output_layout_add does not like disabled outputs */ + if (!test && wlr_output->enabled && (m->m.x != config_head->state.x || m->m.y != config_head->state.y)) + wlr_output_layout_add(output_layout, wlr_output, + config_head->state.x, config_head->state.y); + + wlr_output_state_finish(&state); + } + + if (ok) + wlr_output_configuration_v1_send_succeeded(config); + else + wlr_output_configuration_v1_send_failed(config); + wlr_output_configuration_v1_destroy(config); + + /* TODO: use a wrapper function? */ + updatemons(NULL, NULL); +} + +void //0.5 +outputmgrtest(struct wl_listener *listener, void *data) +{ + struct wlr_output_configuration_v1 *config = data; + outputmgrapplyortest(config, 1); +} + + +void //17 +pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, + uint32_t time) +{ + struct timespec now; + int internal_call = !time; + + if (surface != seat->pointer_state.focused_surface && + sloppyfocus && time && c && !client_is_unmanaged(c)) + focusclient(c, 0); + + /* If surface is NULL, try use the focused client surface to set pointer foucs */ + if (time == 0 && !surface && selmon && selmon->sel) { + surface = client_surface(selmon->sel); + } + + /* If surface is still NULL, clear pointer focus */ + if (!surface) { + wlr_seat_pointer_notify_clear_focus(seat); + return; + } + + if (internal_call) { + clock_gettime(CLOCK_MONOTONIC, &now); + time = now.tv_sec * 1000 + now.tv_nsec / 1000000; + } + + /* Let the client know that the mouse cursor has entered one + * of its surfaces, and make keyboard focus follow if desired. + * wlroots makes this a no-op if surface is already focused */ + wlr_seat_pointer_notify_enter(seat, surface, sx, sy); + wlr_seat_pointer_notify_motion(seat, time, sx, sy); + +} + +void //17 +printstatus(void) +{ + Monitor *m = NULL; + Client *c; + uint32_t occ, urg, sel; + const char *appid, *title; + + wl_list_for_each(m, &mons, link) { + if(!m->wlr_output->enabled) { + continue; + } + occ = urg = 0; + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + if ((c = focustop(m))) { + title = client_get_title(c); + appid = client_get_appid(c); + printf("%s title %s\n", m->wlr_output->name, title ? title : broken); + printf("%s appid %s\n", m->wlr_output->name, appid ? appid : broken); + printf("%s fullscreen %u\n", m->wlr_output->name, c->isfullscreen); + printf("%s floating %u\n", m->wlr_output->name, c->isfloating); + sel = c->tags; + } else { + printf("%s title \n", m->wlr_output->name); + printf("%s appid \n", m->wlr_output->name); + printf("%s fullscreen \n", m->wlr_output->name); + printf("%s floating \n", m->wlr_output->name); + sel = 0; + } + + printf("%s selmon %u\n", m->wlr_output->name, m == selmon); + printf("%s tags %u %u %u %u\n", m->wlr_output->name, occ, m->tagset[m->seltags], + sel, urg); + printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol); + dwl_ipc_output_printstatus(m); //更新waybar上tag的状态 这里很关键 + } + fflush(stdout); +} + +void //0.5 +quit(const Arg *arg) +{ + wl_display_terminate(dpy); +} + +void +quitsignal(int signo) +{ + quit(NULL); +} + +void // 0.5 +rendermon(struct wl_listener *listener, void *data) +{ + /* This function is called every time an output is ready to display a frame, + * generally at the output's refresh rate (e.g. 60Hz). */ + Monitor *m = wl_container_of(listener, m, frame); + Client *c; + struct wlr_output_state pending = {0}; + struct wlr_gamma_control_v1 *gamma_control; + struct timespec now; + bool need_more_frames = false; + + /* Render if no XDG clients have an outstanding resize and are visible on + * this monitor. */ + // wl_list_for_each(c, &clients, link) { + // if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c)) + // goto skip; + // } + + wl_list_for_each(c, &clients, link) + { + // if (client_is_rendered_on_mon(c, m) && !client_is_stopped(c)) + need_more_frames = client_draw_frame(c); + } + + if (need_more_frames) + { + wlr_output_schedule_frame(m->wlr_output); + } + /* + * HACK: The "correct" way to set the gamma is to commit it together with + * the rest of the state in one go, but to do that we would need to rewrite + * wlr_scene_output_commit() in order to add the gamma to the pending + * state before committing, instead try to commit the gamma in one frame, + * and commit the rest of the state in the next one (or in the same frame if + * the gamma can not be committed). + */ + if (m->gamma_lut_changed) + { + gamma_control = wlr_gamma_control_manager_v1_get_control(gamma_control_mgr, m->wlr_output); + m->gamma_lut_changed = 0; + + if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) + goto commit; + + if (!wlr_output_test_state(m->wlr_output, &pending)) + { + wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); + goto commit; + } + wlr_output_commit_state(m->wlr_output, &pending); + wlr_output_schedule_frame(m->wlr_output); + } + else + { + commit: + wlr_scene_output_commit(m->scene_output, NULL); + } + + // skip: + // /* Let clients know a frame has been rendered */ + // clock_gettime(CLOCK_MONOTONIC, &now); + // wlr_scene_output_send_frame_done(m->scene_output, &now); + // wlr_output_state_finish(&pending); + struct wlr_scene_output *scene_output = wlr_scene_get_scene_output(scene, m->wlr_output); + + wlr_scene_output_commit(scene_output, NULL); + + // struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + wlr_scene_output_send_frame_done(scene_output, &now); + wlr_output_state_finish(&pending); +} + +void //0.5 +requestdecorationmode(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, set_decoration_mode); + wlr_xdg_toplevel_decoration_v1_set_mode(c->decoration, + WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); +} + +void //17 +requeststartdrag(struct wl_listener *listener, void *data) +{ + struct wlr_seat_request_start_drag_event *event = data; + + if (wlr_seat_validate_pointer_grab_serial(seat, event->origin, + event->serial)) + wlr_seat_start_pointer_drag(seat, event->drag, event->serial); + else + wlr_data_source_destroy(event->drag->source); +} + + +void //为了兼容dwm代码封装的 +resizeclient(Client *c,int x,int y,int w,int h, int interact) +{ + struct wlr_box tmp_box; + c->prev = c->geom; + tmp_box.x = x; + tmp_box.y = y; + tmp_box.width = w; + tmp_box.height = h; + resize(c, tmp_box, interact); +} + + +void setborder_color(Client *c){ + unsigned int i; + if(!c || !c->mon) + return; + if (c->isurgent) { + for (i = 0; i < 4; i++) + wlr_scene_rect_set_color(c->border[i], urgentcolor); + } if (c->is_in_scratchpad && c == selmon->sel) { + for (i = 0; i < 4; i++) + wlr_scene_rect_set_color(c->border[i], scratchpadcolor); + } else if(c->isglobal && c == selmon->sel){ + for (i = 0; i < 4; i++) + wlr_scene_rect_set_color(c->border[i], globalcolor); + } else if(c->isfakefullscreen && c == selmon->sel){ + for (i = 0; i < 4; i++) + wlr_scene_rect_set_color(c->border[i], fakefullscreencolor); + } else if(c == selmon->sel) { + for (i = 0; i < 4; i++) + wlr_scene_rect_set_color(c->border[i], focuscolor); + } else { + for (i = 0; i < 4; i++) + wlr_scene_rect_set_color(c->border[i], bordercolor); + } +} + +void exchange_two_client(Client *c1, Client *c2) { + if (c1 == NULL || c2 == NULL || c1->mon != c2->mon) { + return; + } + + struct wl_list *tmp1_prev = c1->link.prev; + struct wl_list *tmp2_prev = c2->link.prev; + struct wl_list *tmp1_next = c1->link.next; + struct wl_list *tmp2_next = c2->link.next; + + // wl_list 是双向链表,其中clients是头部节点,它的下一个节点是第一个客户端的链表节点 + //最后一个客户端的链表节点的下一个节点也指向clients,但clients本身不是客户端的链表节点 + //客户端遍历从clients的下一个节点开始,到检测到客户端节点的下一个是clients结束 + + // 当c1和c2为相邻节点时 + if (c1->link.next == &c2->link) { + c1->link.next = c2->link.next; + c1->link.prev = &c2->link; + c2->link.next = &c1->link; + c2->link.prev = tmp1_prev; + tmp1_prev->next = &c2->link; + tmp2_next->prev = &c1->link; + } else if (c2->link.next == &c1->link) { + c2->link.next = c1->link.next; + c2->link.prev = &c1->link; + c1->link.next = &c2->link; + c1->link.prev = tmp2_prev; + tmp2_prev->next = &c1->link; + tmp1_next->prev = &c2->link; + } else { // 不为相邻节点 + c2->link.next = tmp1_next; + c2->link.prev = tmp1_prev; + c1->link.next = tmp2_next; + c1->link.prev = tmp2_prev; + + tmp1_prev->next = &c2->link; + tmp1_next->prev = &c2->link; + tmp2_prev->next = &c1->link; + tmp2_next->prev = &c1->link; + } + + arrange(c1->mon); + focusclient(c1,0); +} + +void exchange_client(const Arg *arg) { + Client *c = selmon->sel; + if (!c || c->isfloating || c->isfullscreen || c->isfakefullscreen) + return; + exchange_two_client(c, direction_select(arg)); +} + + +void +resize(Client *c, struct wlr_box geo, int interact) +{ + + if (!c || !c->mon || !client_surface(c)->mapped) + return; + + struct wlr_box *bbox; + // struct wlr_box clip; + + if (!c->mon) + return; + bbox = interact ? &sgeom : &c->mon->w; + if(c->is_first_resize) { + c->animainit_geom.width = geo.width * zoom_initial_ratio; + c->animainit_geom.height = geo.height * zoom_initial_ratio; + c->animainit_geom.x = geo.x + (geo.width - c->animainit_geom.width)/2; + c->animainit_geom.y = geo.y + (geo.height - c->animainit_geom.height)/2; + } else { + c->animainit_geom = c->geom; + } + client_set_bounds(c, geo.width, geo.height); //去掉这个推荐的窗口大小,因为有时推荐的窗口特别大导致平铺异常 + c->geom = geo; + applybounds(c, bbox);//去掉这个推荐的窗口大小,因为有时推荐的窗口特别大导致平铺异常 + + + if(c->isnoborder) { + c->bw = 0; + } + + /* Update scene-graph, including borders */ + // wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); + // wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); + // wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw); + // wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw); + // wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw); + // wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw); + // wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw); + // wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw); + // wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw); + // wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw); + // wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw); + // wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw); + + // if(!c->isnoclip){ + // c->resize = client_set_size(c, c->geom.width - 2 * c->bw, + // c->geom.height - 2 * c->bw); + // // client_get_clip(c, &clip); + // // wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); + // }else { + // /* this is a no-op if size hasn't changed */ + // c->resize = client_set_size(c, c->geom.width - 2 * c->bw, + // c->geom.height - 2 * c->bw); + // } + // apply_border(c,c->geom); + c->resize = client_set_size(c, c->geom.width - 2 * c->bw, + c->geom.height - 2 * c->bw); + client_set_pending_state(c, geo.x,geo.y,geo.width, geo.height); + + setborder_color(c); +} + +void //17 +run(char *startup_cmd) +{ + /* Add a Unix socket to the Wayland display. */ + const char *socket = wl_display_add_socket_auto(dpy); + if (!socket) + die("startup: display_add_socket_auto"); + setenv("WAYLAND_DISPLAY", socket, 1); + + /* Start the backend. This will enumerate outputs and inputs, become the DRM + * master, etc */ + if (!wlr_backend_start(backend)) + die("startup: backend_start"); + + /* Now that the socket exists and the backend is started, run the startup command */ + autostartexec(); + if (startup_cmd) { + int piperw[2]; + if (pipe(piperw) < 0) + die("startup: pipe:"); + if ((child_pid = fork()) < 0) + die("startup: fork:"); + if (child_pid == 0) { + dup2(piperw[0], STDIN_FILENO); + close(piperw[0]); + close(piperw[1]); + execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL); + die("startup: execl:"); + } + dup2(piperw[1], STDOUT_FILENO); + close(piperw[1]); + close(piperw[0]); + } + printstatus(); + + /* At this point the outputs are initialized, choose initial selmon based on + * cursor position, and set default cursor image */ + selmon = xytomon(cursor->x, cursor->y); + + /* TODO hack to get cursor to display in its initial location (100, 100) + * instead of (0, 0) and then jumping. still may not be fully + * initialized, as the image/coordinates are not transformed for the + * monitor when displayed here */ + wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y); + wlr_cursor_set_xcursor(cursor, cursor_mgr, "left_ptr"); + + /* Run the Wayland event loop. This does not return until you exit the + * compositor. Starting the backend rigged up all of the necessary event + * loop configuration to listen to libinput events, DRM events, generate + * frame events at the refresh rate, and so on. */ + wl_display_run(dpy); +} + +void +setcursor(struct wl_listener *listener, void *data) +{ + /* This event is raised by the seat when a client provides a cursor image */ + struct wlr_seat_pointer_request_set_cursor_event *event = data; + /* If we're "grabbing" the cursor, don't use the client's image, we will + * restore it after "grabbing" sending a leave event, followed by a enter + * event, which will result in the client requesting set the cursor surface */ + if (cursor_mode != CurNormal && cursor_mode != CurPressed) + return; + /* This can be sent by any client, so we check to make sure this one is + * actually has pointer focus first. If so, we can tell the cursor to + * use the provided surface as the cursor image. It will set the + * hardware cursor on the output that it's currently on and continue to + * do so as the cursor moves between outputs. */ + if (event->seat_client == seat->pointer_state.focused_client){ + wlr_cursor_set_surface(cursor, event->surface, + event->hotspot_x, event->hotspot_y); + } + +} + +void //0.5 +setfloating(Client *c, int floating) +{ + + Client *fc; + c->isfloating = floating; + + if (!c || !c->mon || !client_surface(c)->mapped) + return; + + wlr_scene_node_reparent(&c->scene->node, layers[c->isfloating ? LyrFloat : LyrTile]); + + + if (floating == 1) { + if(c->istiled) { + c->geom.height = c->geom.height * 0.8; + c->geom.width = c->geom.width * 0.8; + } + //重新计算居中的坐标 + c->geom = setclient_coordinate_center(c->geom); + applyrulesgeom(c); + if (c->oldgeom.width > 0 && c->oldgeom.height >0) { + resize(c,c->oldgeom,0); + } else { + resize(c,c->geom,0); + } + c->istiled = 0; + } else { + c->istiled = 1; + c->is_scratchpad_show = 0; + c->is_in_scratchpad = 0; + //让当前tag中的全屏窗口退出全屏参与平铺 + wl_list_for_each(fc, &clients, link) + if (fc && c->tags & fc->tags && ISFULLSCREEN(fc)) { + clear_fullscreen_flag(fc); + } + } + + arrange(c->mon); + printstatus(); +} + +void +setfakefullscreen(Client *c, int fakefullscreen) +{ + struct wlr_box fakefullscreen_box; + if (!c || !c->mon || !client_surface(c)->mapped) + return; + + c->isfakefullscreen = fakefullscreen; + + // c->bw = fullscreen ? 0 : borderpx; + // client_set_fullscreen(c, fakefullscreen); + wlr_scene_node_reparent(&c->scene->node, layers[fakefullscreen + ? LyrTile : c->isfloating ? LyrFloat : LyrTile]); + + if (fakefullscreen) { + if (c->isfloating) + c->oldgeom = c->geom; + if (selmon->isoverview) { + Arg arg = {0}; + toggleoverview(&arg); + } + + c->prev = c->geom; + fakefullscreen_box.x = c->mon->w.x + gappov; + fakefullscreen_box.y = c->mon->w.y + gappoh; + fakefullscreen_box.width = c->mon->w.width - 2 * gappov; + fakefullscreen_box.height = c->mon->w.height - 2 * gappov; + wlr_scene_node_raise_to_top(&c->scene->node); //将视图提升到顶层 + resize(c, fakefullscreen_box, 0); + c->isfakefullscreen = 1; + // c->isfloating = 0; + } else { + /* restore previous size instead of arrange for floating windows since + * client positions are set by the user and cannot be recalculated */ + // resize(c, c->prev, 0); + c->bw = borderpx; + c->isfakefullscreen = 0; + c->isfullscreen = 0; + client_set_fullscreen(c, false); + if (c->isfloating) setfloating(c,1); + arrange(c->mon); + } +} + +void +setfullscreen(Client *c, int fullscreen) //用自定义全屏代理自带全屏 +{ + c->isfullscreen = fullscreen; + + if (!c || !c->mon || !client_surface(c)->mapped) + return; + + client_set_fullscreen(c, fullscreen); + wlr_scene_node_reparent(&c->scene->node, layers[fullscreen + ? LyrTile : c->isfloating ? LyrFloat : LyrTile]); + + if (fullscreen) { + if (c->isfloating) + c->oldgeom = c->geom; + if (selmon->isoverview) { + Arg arg = {0}; + toggleoverview(&arg); + } + + c->bw = 0; + wlr_scene_node_raise_to_top(&c->scene->node); //将视图提升到顶层 + resize(c, c->mon->m, 0); + c->isfullscreen = 1; + // c->isfloating = 0; + } else { + /* restore previous size instead of arrange for floating windows since + * client positions are set by the user and cannot be recalculated */ + // resize(c, c->prev, 0); + c->bw = borderpx; + c->isfullscreen = 0; + c->isfullscreen = 0; + c->isfakefullscreen = 0; + if (c->isfloating) setfloating(c,1); + arrange(c->mon); + } +} + + +void +setgaps(int oh, int ov, int ih, int iv) +{ + selmon->gappoh = MAX(oh, 0); + selmon->gappov = MAX(ov, 0); + selmon->gappih = MAX(ih, 0); + selmon->gappiv = MAX(iv, 0); + arrange(selmon); +} + +void //17 +setlayout(const Arg *arg) +{ + if (!selmon) + return; + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt ^= 1; + if (arg && arg->v) { + selmon->lt[selmon->sellt] = (Layout *)arg->v; + selmon->pertag->sellts[selmon->pertag->curtag] = selmon->sellt; + selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = selmon->lt[selmon->sellt]; + } + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol)); + arrange(selmon); + printstatus(); +} + + +void +switch_layout(const Arg *arg) +{ + if (!selmon) + return; + + selmon->sellt ^= 1; + selmon->pertag->sellts[selmon->pertag->curtag] = selmon->sellt; + + /* TODO change layout symbol? */ + arrange(selmon); + printstatus(); +} + +/* arg > 1.0 will set mfact absolutely */ +/* arg > 1.0 will set mfact absolutely */ +void //17 +setmfact(const Arg *arg) +{ + float f; + + if (!arg || !selmon || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.1 || f > 0.9) + return; + selmon->mfact = f; + arrange(selmon); +} + +void //0.5 +setmon(Client *c, Monitor *m, uint32_t newtags) +{ + Monitor *oldmon = c->mon; + + if (oldmon == m) + return; + c->mon = m; + c->prev = c->geom; + + /* Scene graph sends surface leave/enter events on move and resize */ + if (oldmon) + arrange(oldmon); + if (m) { + /* Make sure window actually overlaps with the monitor */ + resize(c, c->geom, 0); + c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ + setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */ + setfloating(c, c->isfloating); + } + focusclient(focustop(selmon), 1); +} + +void //17 +setpsel(struct wl_listener *listener, void *data) +{ + /* This event is raised by the seat when a client wants to set the selection, + * usually when the user copies something. wlroots allows compositors to + * ignore such requests if they so choose, but in dwl we always honor + */ + struct wlr_seat_request_set_primary_selection_event *event = data; + wlr_seat_set_primary_selection(seat, event->source, event->serial); +} + +void //17 +setsel(struct wl_listener *listener, void *data) +{ + /* This event is raised by the seat when a client wants to set the selection, + * usually when the user copies something. wlroots allows compositors to + * ignore such requests if they so choose, but in dwl we always honor + */ + struct wlr_seat_request_set_selection_event *event = data; + wlr_seat_set_selection(seat, event->source, event->serial); +} + +//获取tags中最坐标的tag的tagmask +unsigned int +get_tags_first_tag(unsigned int tags){ + unsigned int i,target,tag; + tag = 0; + for(i=0;!(tag & 1);i++){ + tag = tags >> i; + } + target = 1 << (i-1); + return target; +} + +void show_hide_client(Client *c) { + unsigned int target = get_tags_first_tag(c->oldtags); + tag_client(&(Arg){.ui = target},c); + // c->tags = c->oldtags; + c->isminied = 0; + wlr_foreign_toplevel_handle_v1_set_minimized(c->foreign_toplevel,false); + focusclient(c,1); + wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel,true); +} + +void +handle_foreign_activate_request(struct wl_listener *listener, void *data) { + Client *c = wl_container_of(listener, c, foreign_activate_request); + unsigned int target; + if(c && !c->isminied && c == selmon->sel) { + set_minized(c); + return; + } + + if(c->isminied) { + c->is_in_scratchpad = 0; + c->is_scratchpad_show = 0; + setborder_color(c); + show_hide_client(c); + return; + } + + target = get_tags_first_tag(c->tags); + view(&(Arg){.ui = target}); + focusclient(c,1); + wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel,true); + +} + +void +handle_foreign_fullscreen_request( + struct wl_listener *listener, void *data) { + return; +} + +void +handle_foreign_close_request(struct wl_listener *listener, void *data) { + Client *c = wl_container_of(listener, c, foreign_close_request); + if(c) { + c->iskilling = 1; + client_send_close(c); + } +} + +void +handle_foreign_destroy(struct wl_listener *listener, void *data) { + Client *c = wl_container_of(listener, c, foreign_destroy); + if(c){ + wl_list_remove(&c->foreign_activate_request.link); + wl_list_remove(&c->foreign_fullscreen_request.link); + wl_list_remove(&c->foreign_close_request.link); + wl_list_remove(&c->foreign_destroy.link); + } +} + +void +setup(void) +{ + init_baked_points(); + + int i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE}; + struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig}; + sigemptyset(&sa.sa_mask); + + for (i = 0; i < LENGTH(sig); i++) + sigaction(sig[i], &sa, NULL); + + wlr_log_init(log_level, NULL); + + /* The Wayland display is managed by libwayland. It handles accepting + * clients from the Unix socket, manging Wayland globals, and so on. */ + dpy = wl_display_create(); + pointer_manager = wlr_relative_pointer_manager_v1_create(dpy); + /* The backend is a wlroots feature which abstracts the underlying input and + * output hardware. The autocreate option will choose the most suitable + * backend based on the current environment, such as opening an X11 window + * if an X11 server is running. The NULL argument here optionally allows you + * to pass in a custom renderer if wlr_renderer doesn't meet your needs. The + * backend uses the renderer, for example, to fall back to software cursors + * if the backend does not support hardware cursors (some older GPUs + * don't). */ + if (!(backend = wlr_backend_autocreate(dpy, &session))) + die("couldn't create backend"); + + /* Initialize the scene graph used to lay out windows */ + scene = wlr_scene_create(); + for (i = 0; i < NUM_LAYERS; i++) + layers[i] = wlr_scene_tree_create(&scene->tree); + drag_icon = wlr_scene_tree_create(&scene->tree); + wlr_scene_node_place_below(&drag_icon->node, &layers[LyrBlock]->node); + + /* Create a renderer with the default implementation */ + if (!(drw = wlr_renderer_autocreate(backend))) + die("couldn't create renderer"); + + /* Create shm, drm and linux_dmabuf interfaces by ourselves. + * The simplest way is call: + * wlr_renderer_init_wl_display(drw); + * but we need to create manually the linux_dmabuf interface to integrate it + * with wlr_scene. */ + wlr_renderer_init_wl_shm(drw, dpy); + + if (wlr_renderer_get_dmabuf_texture_formats(drw)) { + wlr_drm_create(dpy, drw); + wlr_scene_set_linux_dmabuf_v1(scene, + wlr_linux_dmabuf_v1_create_with_renderer(dpy, 4, drw)); + } + + /* Create a default allocator */ + if (!(alloc = wlr_allocator_autocreate(backend, drw))) + die("couldn't create allocator"); + + /* This creates some hands-off wlroots interfaces. The compositor is + * necessary for clients to allocate surfaces and the data device manager + * handles the clipboard. Each of these wlroots interfaces has room for you + * to dig your fingers in and play with their behavior if you want. Note that + * the clients cannot set the selection directly without compositor approval, + * see the setsel() function. */ + compositor = wlr_compositor_create(dpy, 6, drw); + wlr_export_dmabuf_manager_v1_create(dpy); + wlr_screencopy_manager_v1_create(dpy); + wlr_data_control_manager_v1_create(dpy); + wlr_data_device_manager_create(dpy); + wlr_primary_selection_v1_device_manager_create(dpy); + wlr_viewporter_create(dpy); + wlr_single_pixel_buffer_manager_v1_create(dpy); + wlr_fractional_scale_manager_v1_create(dpy, 1); + wlr_subcompositor_create(dpy); + + /* Initializes the interface used to implement urgency hints */ + activation = wlr_xdg_activation_v1_create(dpy); + LISTEN_STATIC(&activation->events.request_activate, urgent); + + gamma_control_mgr = wlr_gamma_control_manager_v1_create(dpy); + LISTEN_STATIC(&gamma_control_mgr->events.set_gamma, setgamma); + + /* Creates an output layout, which a wlroots utility for working with an + * arrangement of screens in a physical layout. */ + output_layout = wlr_output_layout_create(); + LISTEN_STATIC(&output_layout->events.change, updatemons); + wlr_xdg_output_manager_v1_create(dpy, output_layout); + + /* Configure a listener to be notified when new outputs are available on the + * backend. */ + wl_list_init(&mons); + LISTEN_STATIC(&backend->events.new_output, createmon); + + /* Set up our client lists and the xdg-shell. The xdg-shell is a + * Wayland protocol which is used for application windows. For more + * detail on shells, refer to the article: + * + * https://drewdevault.com/2018/07/29/Wayland-shells.html + */ + wl_list_init(&clients); + wl_list_init(&fstack); + + idle_notifier = wlr_idle_notifier_v1_create(dpy); + + idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy); + LISTEN_STATIC(&idle_inhibit_mgr->events.new_inhibitor, createidleinhibitor); + + layer_shell = wlr_layer_shell_v1_create(dpy, 3); + LISTEN_STATIC(&layer_shell->events.new_surface, createlayersurface); + + xdg_shell = wlr_xdg_shell_create(dpy, 6); + LISTEN_STATIC(&xdg_shell->events.new_surface, createnotify); + + session_lock_mgr = wlr_session_lock_manager_v1_create(dpy); + wl_signal_add(&session_lock_mgr->events.new_lock, &lock_listener); + LISTEN_STATIC(&session_lock_mgr->events.destroy, destroysessionmgr); + locked_bg = wlr_scene_rect_create(layers[LyrBlock], sgeom.width, sgeom.height, + (float [4]){0.1, 0.1, 0.1, 1.0}); + wlr_scene_node_set_enabled(&locked_bg->node, 0); + + /* Use decoration protocols to negotiate server-side decorations */ + wlr_server_decoration_manager_set_default_mode( + wlr_server_decoration_manager_create(dpy), + WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); + xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy); + LISTEN_STATIC(&xdg_decoration_mgr->events.new_toplevel_decoration, createdecoration); + pointer_constraints = wlr_pointer_constraints_v1_create(dpy); + LISTEN_STATIC(&pointer_constraints->events.new_constraint, createpointerconstraint); + + relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy); + + /* + * Creates a cursor, which is a wlroots utility for tracking the cursor + * image shown on screen. + */ + cursor = wlr_cursor_create(); + wlr_cursor_attach_output_layout(cursor, output_layout); + + /* Creates an xcursor manager, another wlroots utility which loads up + * Xcursor themes to source cursor images from and makes sure that cursor + * images are available at all scale factors on the screen (necessary for + * HiDPI support). Scaled cursors will be loaded with each output. */ + // cursor_mgr = wlr_xcursor_manager_create(cursor_theme, 24); + cursor_mgr = wlr_xcursor_manager_create(NULL, 24); + setenv("XCURSOR_SIZE", "24", 1); + + /* + * wlr_cursor *only* displays an image on screen. It does not move around + * when the pointer moves. However, we can attach input devices to it, and + * it will generate aggregate events for all of them. In these events, we + * can choose how we want to process them, forwarding them to clients and + * moving the cursor around. More detail on this process is described in my + * input handling blog post: + * + * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html + * + * And more comments are sprinkled throughout the notify functions above. + */ + LISTEN_STATIC(&cursor->events.motion, motionrelative); + LISTEN_STATIC(&cursor->events.motion_absolute, motionabsolute); + LISTEN_STATIC(&cursor->events.button, buttonpress); + LISTEN_STATIC(&cursor->events.axis, axisnotify); + LISTEN_STATIC(&cursor->events.frame, cursorframe); + + //这两句代码会造成obs窗口里的鼠标光标消失,不知道注释有什么影响 + cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1); + LISTEN_STATIC(&cursor_shape_mgr->events.request_set_shape, setcursorshape); + + /* + * Configures a seat, which is a single "seat" at which a user sits and + * operates the computer. This conceptually includes up to one keyboard, + * pointer, touch, and drawing tablet device. We also rig up a listener to + * let us know when new input devices are available on the backend. + */ + wl_list_init(&keyboards); + LISTEN_STATIC(&backend->events.new_input, inputdevice); + virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy); + LISTEN_STATIC(&virtual_keyboard_mgr->events.new_virtual_keyboard, virtualkeyboard); + virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(dpy); + LISTEN_STATIC(&virtual_pointer_mgr->events.new_virtual_pointer, virtualpointer); + + seat = wlr_seat_create(dpy, "seat0"); + LISTEN_STATIC(&seat->events.request_set_cursor, setcursor); + LISTEN_STATIC(&seat->events.request_set_selection, setsel); + LISTEN_STATIC(&seat->events.request_set_primary_selection, setpsel); + LISTEN_STATIC(&seat->events.request_start_drag, requeststartdrag); + LISTEN_STATIC(&seat->events.start_drag, startdrag); + + output_mgr = wlr_output_manager_v1_create(dpy); + LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply); + LISTEN_STATIC(&output_mgr->events.test, outputmgrtest); + + wlr_scene_set_presentation(scene, wlr_presentation_create(dpy, backend)); +#ifdef IM + /* create text_input-, and input_method-protocol relevant globals */ + input_method_manager = wlr_input_method_manager_v2_create(dpy); + text_input_manager = wlr_text_input_manager_v3_create(dpy); + + input_relay = calloc(1, sizeof(*input_relay)); + dwl_input_method_relay_init(input_relay); +#endif + wl_global_create(dpy, &zdwl_ipc_manager_v2_interface, 1, NULL, dwl_ipc_manager_bind); + + //创建顶层管理句柄 + foreign_toplevel_manager =wlr_foreign_toplevel_manager_v1_create(dpy); + struct wlr_xdg_foreign_registry *foreign_registry = wlr_xdg_foreign_registry_create(dpy); + wlr_xdg_foreign_v1_create(dpy, foreign_registry); + wlr_xdg_foreign_v2_create(dpy, foreign_registry); +#ifdef XWAYLAND + /* + * Initialise the XWayland X server. + * It will be started when the first X client is started. + */ + xwayland = wlr_xwayland_create(dpy, compositor, 1); + if (xwayland) { + LISTEN_STATIC(&xwayland->events.ready, xwaylandready); + LISTEN_STATIC(&xwayland->events.new_surface, createnotifyx11); + + setenv("DISPLAY", xwayland->display_name, 1); + } else { + fprintf(stderr, "failed to setup XWayland X server, continuing without it\n"); + } +#endif +} + +void +sigchld(int unused) +{ + siginfo_t in; + /* We should be able to remove this function in favor of a simple + * struct sigaction sa = {.sa_handler = SIG_IGN}; + * sigaction(SIGCHLD, &sa, NULL); + * but the Xwayland implementation in wlroots currently prevents us from + * setting our own disposition for SIGCHLD. + */ + /* WNOWAIT leaves the child in a waitable state, in case this is the + * XWayland process + */ + while (!waitid(P_ALL, 0, &in, WEXITED|WNOHANG|WNOWAIT) && in.si_pid +#ifdef XWAYLAND + && (!xwayland || in.si_pid != xwayland->server->pid) +#endif + ) { + pid_t *p, *lim; + waitpid(in.si_pid, NULL, 0); + if (in.si_pid == child_pid) + child_pid = -1; + if (!(p = autostart_pids)) + continue; + lim = &p[autostart_len]; + + for (; p < lim; p++) { + if (*p == in.si_pid) { + *p = -1; + break; + } + } + } +} + +void //17 +spawn(const Arg *arg) +{ + if (fork() == 0) { + dup2(STDERR_FILENO, STDOUT_FILENO); + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + die("dwl: execvp %s failed:", ((char **)arg->v)[0]); + } +} + + +void +startdrag(struct wl_listener *listener, void *data) +{ + struct wlr_drag *drag = data; + if (!drag->icon) + return; + + drag->icon->data = &wlr_scene_drag_icon_create(drag_icon, drag->icon)->node; + LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon); +} + + +void tag_client(const Arg *arg, Client *target_client) { + Client *fc; + if (target_client && arg->ui & TAGMASK) { + target_client->tags = arg->ui & TAGMASK; + wl_list_for_each(fc, &clients, link){ + if (fc && fc != target_client && target_client->tags & fc->tags && ISFULLSCREEN(fc) && !target_client->isfloating ) { + clear_fullscreen_flag(fc); + } + } + view(&(Arg){.ui = arg->ui}); + + } else{ + view(arg); + } + + focusclient(target_client,1); + printstatus(); +} + +void +tag(const Arg *arg) { + Client *target_client = selmon->sel; + tag_client(arg,target_client); +} + + +void +tagmon(const Arg *arg) +{ + Client *c = focustop(selmon); + if (c) { + setmon(c, dirtomon(arg->i), 0); + reset_foreign_tolevel(c); + selmon = c->mon; + c->geom.width = (int)(c->geom.width * selmon->m.width/c->mon->m.width); + c->geom.height = (int)(c->geom.height * selmon->m.height/c->mon->m.height); + //重新计算居中的坐标 + if(c->isfloating) { + c->geom = setclient_coordinate_center(c->geom); + resize(c,c->geom,0); + } + warp_cursor_to_selmon(c->mon); + focusclient(c,1); + } + +} + +void +overview(Monitor *m, unsigned int gappo, unsigned int gappi) { + grid(m, overviewgappo, overviewgappi); +} + +// 网格布局窗口大小和位置计算 +void grid(Monitor *m, unsigned int gappo, unsigned int gappi) { + unsigned int i, n; + unsigned int cx, cy, cw, ch; + unsigned int dx; + unsigned int cols, rows, overcols; + Client *c; + Client *tempClients[100]; + n = 0; + wl_list_for_each(c, &clients, link) + if (VISIBLEON(c, c->mon) && c->mon == selmon){ + tempClients[n] = c; + n++; + } + tempClients[n] = NULL; + if (n == 0) + return; + if (n == 1) { + c = tempClients[0]; + cw = (m->w.width - 2 * gappo) * 0.7; + ch = (m->w.height- 2 * gappo) * 0.8; + resizeclient(c, m->w.x + (m->m.width - cw) / 2, m->w.y + (m->w.height- ch) / 2, + cw - 2 * c->bw, ch - 2 * c->bw, 0); + return; + } + if (n == 2) { + cw = (m->w.width - 2 * gappo - gappi) / 2; + ch = (m->w.height- 2 * gappo) * 0.65; + resizeclient(tempClients[1], m->m.x + cw + gappo + gappi, m->m.y + (m->m.height - ch) / 2 + gappo, + cw - 2 * tempClients[1]->bw, ch - 2 * tempClients[1]->bw, 0); + resizeclient(tempClients[0], m->m.x + gappo, m->m.y + (m->m.height - ch) / 2 + gappo, + cw - 2 * tempClients[0]->bw, ch - 2 * tempClients[0]->bw, 0); + + return; + } + + for (cols = 0; cols <= n / 2; cols++) + if (cols * cols >= n) + break; + rows = (cols && (cols - 1) * cols >= n) ? cols - 1 : cols; + ch = (m->w.height- 2 * gappo - (rows - 1) * gappi) / rows; + cw = (m->w.width - 2 * gappo - (cols - 1) * gappi) / cols; + + overcols = n % cols; + if (overcols) + dx = (m->w.width - overcols * cw - (overcols - 1) * gappi) / 2 - gappo; + for (i = 0, c = tempClients[0]; c; c = tempClients[i+1], i++) { + cx = m->w.x + (i % cols) * (cw + gappi); + cy = m->w.y + (i / cols) * (ch + gappi); + if (overcols && i >= n - overcols) { + cx += dx; + } + resizeclient(c, cx + gappo, cy + gappo, cw - 2 * c->bw, ch - 2 * c->bw, 0); + } +} + +// 目标窗口有其他窗口和它同个tag就返回0 +unsigned int want_restore_fullscreen(Client *target_client) { + Client *c = NULL; + wl_list_for_each(c, &clients, link){ + if (c && c != target_client && c->tags == target_client->tags && c == selmon->sel) { + return 0; + } + } + + return 1; +} + +// 普通视图切换到overview时保存窗口的旧状态 +void overview_backup(Client *c) { + c->overview_isfloatingbak = c->isfloating; + c->overview_isfullscreenbak = c->isfullscreen; + c->overview_isfakefullscreenbak = c->isfakefullscreen; + c->overview_isfullscreenbak = c->isfullscreen; + c->overview_backup_x = c->geom.x; + c->overview_backup_y = c->geom.y; + c->overview_backup_w = c->geom.width; + c->overview_backup_h = c->geom.height; + c->overview_backup_bw = c->bw; + if (c->isfloating) { + c->isfloating = 0; + } + if (c->isfullscreen || c->isfakefullscreen) { + // if (c->bw == 0) { // 真全屏窗口清除x11全屏属性 + // XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + // PropModeReplace, (unsigned char *)0, 0); + // } + c->isfullscreen = 0; // 清除窗口全屏标志 + c->isfakefullscreen = 0; + } + c->bw = borderpx; // 恢复非全屏的border +} + +// overview切回到普通视图还原窗口的状态 +void overview_restore(Client *c, const Arg *arg) { + c->isfloating = c->overview_isfloatingbak; + c->isfullscreen = c->overview_isfullscreenbak; + c->isfakefullscreen = c->overview_isfakefullscreenbak; + c->overview_isfloatingbak = 0; + c->overview_isfullscreenbak = 0; + c->overview_isfakefullscreenbak = 0; + c->bw = c->overview_backup_bw; + if (c->isfloating) { + // XRaiseWindow(dpy, c->win); // 提升悬浮窗口到顶层 + resizeclient(c, c->overview_backup_x, c->overview_backup_y, c->overview_backup_w, + c->overview_backup_h, 1); + } else if (c->isfullscreen ||c->isfakefullscreen) { + if (want_restore_fullscreen(c)) { //如果同tag有其他窗口,且其他窗口是将要聚焦的,那么不恢复该窗口的全屏状态 + resizeclient(c, c->overview_backup_x, c->overview_backup_y, + c->overview_backup_w, c->overview_backup_h,1); + } else { + c->isfullscreen = 0; + c->isfakefullscreen = 0; + client_set_fullscreen(c,false); + } + } else { + resizeclient(c, c->overview_backup_x, c->overview_backup_y, c->overview_backup_w, + c->overview_backup_h, 0); + } + + if(c->bw == 0 && !c->isnoborder && !c->isfullscreen) { //如果是在ov模式中创建的窗口,没有bw记录 + c->bw = borderpx; + } + +} + + +// 显示所有tag 或 跳转到聚焦窗口的tag +void toggleoverview(const Arg *arg) { + + Client *c; + selmon->isoverview ^= 1; + unsigned int target; + unsigned int visible_client_number = 0; + + if(selmon->isoverview){ + wl_list_for_each(c, &clients, link) + if (c && c->mon == selmon && !c->isminied){ + visible_client_number++; + } + if(visible_client_number > 0) { + target = ~0; + } else { + selmon->isoverview ^= 1; + return; + } + }else if(!selmon->isoverview && selmon->sel) { + target = get_tags_first_tag(selmon->sel->tags); + } else if (!selmon->isoverview && !selmon->sel) { + target = (1 << (selmon->pertag->prevtag-1)); + view(&(Arg){.ui = target}); + return; + } + + // 正常视图到overview,退出所有窗口的浮动和全屏状态参与平铺, + // overview到正常视图,还原之前退出的浮动和全屏窗口状态 + if (selmon->isoverview) { + wl_list_for_each(c, &clients, link){ + if(c) + overview_backup(c); + } + } else { + wl_list_for_each(c, &clients, link){ + if(c && (c != selmon->sel || c->overview_isfloatingbak || c->overview_isfullscreenbak || c->overview_isfakefullscreenbak) && !c->iskilling && client_surface(c)->mapped) + overview_restore(c, &(Arg){.ui = target}); + } + } + + view(&(Arg){.ui = target}); + +} + +void +tile(Monitor *m,unsigned int gappo, unsigned int uappi) +{ + unsigned int i, n = 0, h, r, oe = enablegaps, ie = enablegaps, mw, my, ty; + Client *c; + + wl_list_for_each(c, &clients, link) + if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen && !c->isfakefullscreen) + n++; + if (n == 0) + return; + + if (smartgaps == n) { + oe = 0; // outer gaps disabled + } + + if (n > m->nmaster) + mw = m->nmaster ? (m->w.width + m->gappiv*ie) * m->mfact : 0; + else + mw = m->w.width - 2*m->gappov*oe + m->gappiv*ie; + i = 0; + my = ty = m->gappoh*oe; + wl_list_for_each(c, &clients, link) { + if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen || c->isfakefullscreen ) + continue; + if (i < m->nmaster) { + r = MIN(n, m->nmaster) - i; + h = (m->w.height - my - m->gappoh*oe - m->gappih*ie * (r - 1)) / r; + resize(c, (struct wlr_box){.x = m->w.x + m->gappov*oe, .y = m->w.y + my, + .width = mw - m->gappiv*ie, .height = h}, 0); + my += c->geom.height + m->gappih*ie; + } else { + r = n - i; + h = (m->w.height - ty - m->gappoh*oe - m->gappih*ie * (r - 1)) / r; + resize(c, (struct wlr_box){.x = m->w.x + mw + m->gappov*oe, .y = m->w.y + ty, + .width = m->w.width - mw - 2*m->gappov*oe, .height = h}, 0); + ty += c->geom.height + m->gappih*ie; + } + i++; + } +} + + +void +togglefloating(const Arg *arg) +{ + Client *sel = focustop(selmon); + + if(!sel) + return; + + if (sel->isfullscreen || sel->isfakefullscreen) { + sel->isfullscreen = 0; // 清除窗口全屏标志 + sel->isfakefullscreen = 0; + sel->bw = borderpx; // 恢复非全屏的border + } + /* return if fullscreen */ + setfloating(sel, !sel->isfloating); + setborder_color(sel); +} + +void +togglefullscreen(const Arg *arg) +{ + Client *sel = focustop(selmon); + if(!sel) + return; + + // if(sel->isfloating) + // setfloating(sel, 0); + + if (sel->isfullscreen || sel->isfakefullscreen) + setfullscreen(sel, 0); + else + setfullscreen(sel, 1); + + sel->is_scratchpad_show = 0; + sel->is_in_scratchpad = 0; +} + +void +togglefakefullscreen(const Arg *arg) +{ + Client *sel = focustop(selmon); + if(!sel) + return; + + // if(sel->isfloating) + // setfloating(sel, 0); + + if (sel->isfullscreen || sel->isfakefullscreen) + setfakefullscreen(sel, 0); + else + setfakefullscreen(sel, 1); + + sel->is_scratchpad_show = 0; + sel->is_in_scratchpad = 0; +} + +void +togglegaps(const Arg *arg) +{ + enablegaps ^= 1; + arrange(selmon); +} + +void +toggletag(const Arg *arg) +{ + unsigned int newtags; + Client *sel = focustop(selmon); + if (!sel) + return; + newtags = sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { + sel->tags = newtags; + focusclient(focustop(selmon), 1); + arrange(selmon); + } + printstatus(); +} + +void +toggleview(const Arg *arg) +{ + unsigned int newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0; + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focusclient(focustop(selmon), 1); + arrange(selmon); + } + printstatus(); +} + +void // 0.5 +unlocksession(struct wl_listener *listener, void *data) +{ + SessionLock *lock = wl_container_of(listener, lock, unlock); + destroylock(lock, 1); +} + +void // 0.5 +unmaplayersurfacenotify(struct wl_listener *listener, void *data) +{ + LayerSurface *l = wl_container_of(listener, l, unmap); + + l->mapped = 0; + wlr_scene_node_set_enabled(&l->scene->node, 0); + if (l == exclusive_focus) + exclusive_focus = NULL; + if (l->layer_surface->output && (l->mon = l->layer_surface->output->data)) + arrangelayers(l->mon); + if (l->layer_surface->surface == seat->keyboard_state.focused_surface) + focusclient(focustop(selmon), 1); + motionnotify(0, NULL, 0, 0, 0, 0); +} + +void +unmapnotify(struct wl_listener *listener, void *data) +{ + /* Called when the surface is unmapped, and should no longer be shown. */ + Client *c = wl_container_of(listener, c, unmap); + + if (c == selmon->sel) + selmon->sel = NULL; + + if (c == grabc) { + cursor_mode = CurNormal; + grabc = NULL; + } + + if (client_is_unmanaged(c)) { + if (c == exclusive_focus) + exclusive_focus = NULL; + if (client_surface(c) == seat->keyboard_state.focused_surface) + focusclient(focustop(selmon), 1); + } else { + wl_list_remove(&c->link); + setmon(c, NULL, 0); + wl_list_remove(&c->flink); + } + + wlr_scene_node_destroy(&c->scene->node); + if(c->foreign_toplevel){ + wlr_foreign_toplevel_handle_v1_destroy(c->foreign_toplevel); + c->foreign_toplevel = NULL; + } + + Client *nextfocus = focustop(selmon); + + if(nextfocus) { + focusclient(nextfocus,0); + } + + if(!nextfocus && selmon->isoverview){ + Arg arg = {0}; + toggleoverview(&arg); + } + + printstatus(); + motionnotify(0, NULL, 0, 0, 0, 0); +} + +void //0.5 +updatemons(struct wl_listener *listener, void *data) +{ + /* + * Called whenever the output layout changes: adding or removing a + * monitor, changing an output's mode or position, etc. This is where + * the change officially happens and we update geometry, window + * positions, focus, and the stored configuration in wlroots' + * output-manager implementation. + */ + struct wlr_output_configuration_v1 *config + = wlr_output_configuration_v1_create(); + Client *c; + struct wlr_output_configuration_head_v1 *config_head; + Monitor *m; + + /* First remove from the layout the disabled monitors */ + wl_list_for_each(m, &mons, link) { + if (m->wlr_output->enabled) + continue; + config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output); + config_head->state.enabled = 0; + /* Remove this output from the layout to avoid cursor enter inside it */ + wlr_output_layout_remove(output_layout, m->wlr_output); + closemon(m); + m->m = m->w = (struct wlr_box){0}; + } + /* Insert outputs that need to */ + wl_list_for_each(m, &mons, link) { + if (m->wlr_output->enabled + && !wlr_output_layout_get(output_layout, m->wlr_output)) + wlr_output_layout_add_auto(output_layout, m->wlr_output); + } + + /* Now that we update the output layout we can get its box */ + wlr_output_layout_get_box(output_layout, NULL, &sgeom); + + /* Make sure the clients are hidden when dwl is locked */ + wlr_scene_node_set_position(&locked_bg->node, sgeom.x, sgeom.y); + wlr_scene_rect_set_size(locked_bg, sgeom.width, sgeom.height); + + wl_list_for_each(m, &mons, link) { + if (!m->wlr_output->enabled) + continue; + config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output); + + /* Get the effective monitor geometry to use for surfaces */ + wlr_output_layout_get_box(output_layout, m->wlr_output, &m->m); + m->w = m->m; + wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y); + + // wlr_scene_node_set_position(&m->fullscreen_bg->node, m->m.x, m->m.y); + // wlr_scene_rect_set_size(m->fullscreen_bg, m->m.width, m->m.height); + + if (m->lock_surface) { + struct wlr_scene_tree *scene_tree = m->lock_surface->surface->data; + wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y); + wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width, m->m.height); + } + + /* Calculate the effective monitor geometry to use for clients */ + arrangelayers(m); + /* Don't move clients to the left output when plugging monitors */ + arrange(m); + /* make sure fullscreen clients have the right size */ + if ((c = focustop(m)) && c->isfullscreen) + resize(c, m->m, 0); + + /* Try to re-set the gamma LUT when updating monitors, + * it's only really needed when enabling a disabled output, but meh. */ + m->gamma_lut_changed = 1; + + config_head->state.x = m->m.x; + config_head->state.y = m->m.y; + if (!selmon) { + selmon = m; + } + } + + if (selmon && selmon->wlr_output->enabled) { + wl_list_for_each(c, &clients, link) { + if (!c->mon && client_surface(c)->mapped) { + setmon(c, selmon, c->tags); + reset_foreign_tolevel(c); + } + } + focusclient(focustop(selmon), 1); + if (selmon->lock_surface) { + client_notify_enter(selmon->lock_surface->surface, + wlr_seat_get_keyboard(seat)); + client_activate_surface(selmon->lock_surface->surface, 1); + } + } + + /* FIXME: figure out why the cursor image is at 0,0 after turning all + * the monitors on. + * Move the cursor image where it used to be. It does not generate a + * wl_pointer.motion event for the clients, it's only the image what it's + * at the wrong position after all. */ + wlr_cursor_move(cursor, NULL, 0, 0); + + wlr_output_manager_v1_set_configuration(output_mgr, config); +} + +void +updatetitle(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, set_title); + const char *title; + title = client_get_title(c) ; + if(title && c->foreign_toplevel) + wlr_foreign_toplevel_handle_v1_set_title(c->foreign_toplevel,title); + if (c == focustop(c->mon)) + printstatus(); + +} + +void //17 fix to 0.5 +urgent(struct wl_listener *listener, void *data) +{ + struct wlr_xdg_activation_v1_request_activate_event *event = data; + Client *c = NULL; + toplevel_from_wlr_surface(event->surface, &c, NULL); + + if (!c || !c->foreign_toplevel) + return; + + if (focus_on_activate && c != selmon->sel) { + view(&(Arg){.ui = c->tags}); + focusclient(c,1); + } else if(c != focustop(selmon)) { + if (client_surface(c)->mapped) + client_set_border_color(c, urgentcolor); + c->isurgent = 1; + printstatus(); + } +} + +void +view(const Arg *arg) +{ + size_t i, tmptag; + + if(!selmon || (arg->ui != ~0 && selmon->isoverview)){ + return; + } + + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) { + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + selmon->pertag->prevtag = selmon->pertag->curtag; + + if (arg->ui == ~0) + selmon->pertag->curtag = 0; + else { + for (i = 0; !(arg->ui & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + } else { + tmptag = selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = tmptag; + } + + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + focusclient(focustop(selmon), 1); + arrange(selmon); + printstatus(); +} + + +void +viewtoleft(const Arg *arg) +{ + size_t tmptag; + unsigned int target = selmon->tagset[selmon->seltags]; + + if(selmon->isoverview || selmon->pertag->curtag == 0){ + return; + } + + target >>= 1; + + if(target == 0){ + return; + } + + if (!selmon || (target) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ + if (target) { + selmon->tagset[selmon->seltags] = target; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = selmon->pertag->curtag - 1; + } else { + tmptag = selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = tmptag; + } + + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + focusclient(focustop(selmon), 1); + arrange(selmon); + printstatus(); +} + +void +viewtoright_have_client(const Arg *arg) +{ + size_t tmptag; + Client *c; + unsigned int found = 0; + unsigned int n = 1; + unsigned int target = selmon->tagset[selmon->seltags]; + + if(selmon->isoverview || selmon->pertag->curtag == 0){ + return; + } + + for(target <<= 1;target & TAGMASK;target <<= 1,n++){ + wl_list_for_each(c, &clients, link){ + if(target & c->tags){ + found = 1; + break; + } + } + if(found){ + break; + } + } + + if(!(target & TAGMASK)){ + return; + } + + if (!selmon || (target) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ + if (target) { + selmon->tagset[selmon->seltags] = target; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = selmon->pertag->curtag + n; + } else { + tmptag = selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = tmptag; + } + + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + focusclient(focustop(selmon), 1); + arrange(selmon); + printstatus(); +} + +void +viewtoright(const Arg *arg) +{ + if(selmon->isoverview || selmon->pertag->curtag == 0){ + return; + } + size_t tmptag; + unsigned int target = selmon->tagset[selmon->seltags]; + target <<= 1; + + if (!selmon || (target) == selmon->tagset[selmon->seltags]) + return; + if (!(target & TAGMASK)){ + return; + } + selmon->seltags ^= 1; /* toggle sel tagset */ + if (target) { + selmon->tagset[selmon->seltags] = target; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = selmon->pertag->curtag + 1; + } else { + tmptag = selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = tmptag; + } + + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + focusclient(focustop(selmon), 1); + arrange(selmon); + printstatus(); +} + +void +viewtoleft_have_client(const Arg *arg) +{ + size_t tmptag; + Client *c; + unsigned int found = 0; + unsigned int n = 1; + unsigned int target = selmon->tagset[selmon->seltags]; + + if(selmon->isoverview || selmon->pertag->curtag == 0){ + return; + } + + for(target >>= 1;target>0;target >>= 1,n++){ + wl_list_for_each(c, &clients, link){ + if(target & c->tags){ + found = 1; + break; + } + } + if(found){ + break; + } + } + + if(target == 0){ + return; + } + + if (!selmon || (target) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ + if (target) { + selmon->tagset[selmon->seltags] = target; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = selmon->pertag->curtag - n; + } else { + tmptag = selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = tmptag; + } + + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + focusclient(focustop(selmon), 1); + arrange(selmon); + printstatus(); +} + + +void tagtoleft(const Arg *arg) { + if (selmon->sel != NULL && + __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 && + selmon->tagset[selmon->seltags] > 1) { + tag(&(Arg){.ui = selmon->tagset[selmon->seltags] >> 1}); + } +} + +void tagtoright(const Arg *arg) { + if (selmon->sel != NULL && + __builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1 && + selmon->tagset[selmon->seltags] & (TAGMASK >> 1)) { + tag(&(Arg){.ui = selmon->tagset[selmon->seltags] << 1}); + } +} + + +void +virtualkeyboard(struct wl_listener *listener, void *data) +{ + struct wlr_virtual_keyboard_v1 *keyboard = data; + createkeyboard(&keyboard->keyboard); +} + +void +warp_cursor(const Client *c) { + if (cursor->x < c->geom.x || + cursor->x > c->geom.x + c->geom.width || + cursor->y < c->geom.y || + cursor->y > c->geom.y + c->geom.height) + wlr_cursor_warp_closest(cursor, + NULL, + c->geom.x + c->geom.width / 2.0, + c->geom.y + c->geom.height / 2.0); +} + +void +warp_cursor_to_selmon(const Monitor *m) { + + wlr_cursor_warp_closest(cursor, + NULL, + m->w.x + m->w.width / 2.0, + m->w.y + m->w.height / 2.0); +} + +void +virtualpointer(struct wl_listener *listener, void *data) +{ + struct wlr_virtual_pointer_v1_new_pointer_event *event = data; + struct wlr_input_device *device = &event->new_pointer->pointer.base; + + wlr_cursor_attach_input_device(cursor, device); + if (event->suggested_output) + wlr_cursor_map_input_to_output(cursor, device, event->suggested_output); +} + +Monitor * +xytomon(double x, double y) +{ + struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y); + return o ? o->data : NULL; +} + +void +xytonode(double x, double y, struct wlr_surface **psurface, + Client **pc, LayerSurface **pl, double *nx, double *ny) +{ + struct wlr_scene_node *node, *pnode; + struct wlr_surface *surface = NULL; + Client *c = NULL; + LayerSurface *l = NULL; + int layer; + + for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) { + #ifdef IM + if (layer == LyrIMPopup) continue; + #endif + if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny))) + continue; + + if (node->type == WLR_SCENE_NODE_BUFFER) + surface = wlr_scene_surface_try_from_buffer( + wlr_scene_buffer_from_node(node))->surface; + /* Walk the tree to find a node that knows the client */ + for (pnode = node; pnode && !c; pnode = &pnode->parent->node) + c = pnode->data; + if (c && c->type == LayerShell) { + c = NULL; + l = pnode->data; + } + } + + if (psurface) *psurface = surface; + if (pc) *pc = c; + if (pl) *pl = l; +} + +void toggleglobal(const Arg *arg) { + if (!selmon->sel) + return; + if (selmon->sel->is_in_scratchpad) { + selmon->sel->is_in_scratchpad = 0; + selmon->sel->is_scratchpad_show = 0; + } + selmon->sel->isglobal ^= 1; +// selmon->sel->tags = +// selmon->sel->isglobal ? TAGMASK : selmon->tagset[selmon->seltags]; +// focustop(selmon); + setborder_color(selmon->sel); +} + +void +zoom(const Arg *arg) +{ + Client *c, *sel = focustop(selmon); + + if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange || sel->isfloating) + return; + + /* Search for the first tiled window that is not sel, marking sel as + * NULL if we pass it along the way */ + wl_list_for_each(c, &clients, link) + if (VISIBLEON(c, selmon) && !c->isfloating) { + if (c != sel) + break; + sel = NULL; + } + + /* Return if no other tiled window was found */ + if (&c->link == &clients) + return; + + /* If we passed sel, move c to the front; otherwise, move sel to the + * front */ + if (!sel) + sel = c; + wl_list_remove(&sel->link); + wl_list_insert(&clients, &sel->link); + + focusclient(sel, 1); + arrange(selmon); +} + +#ifdef XWAYLAND +void +activatex11(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, activate); + + /* Only "managed" windows can be activated */ + if (!client_is_unmanaged(c)) + wlr_xwayland_surface_activate(c->surface.xwayland, 1); + + if (!c || !c->foreign_toplevel) + return; + + if (focus_on_activate && c != selmon->sel) { + view(&(Arg){.ui = c->tags}); + focusclient(c,1); + } else if(c != focustop(selmon)) { + if (client_surface(c)->mapped) + client_set_border_color(c, urgentcolor); //在使用窗口剪切补丁后,这里启动gdm-settings的字体更改那里点击就会崩溃,增加过滤条件为是toplevel窗口后似乎已经解决 + c->isurgent = 1; + printstatus(); + } +} + +void //0.7 +configurex11(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, configure); + struct wlr_xwayland_surface_configure_event *event = data; + if (!client_surface(c) || !client_surface(c)->mapped) { + wlr_xwayland_surface_configure(c->surface.xwayland, + event->x, event->y, event->width, event->height); + return; + } + if (client_is_unmanaged(c)) { + wlr_scene_node_set_position(&c->scene->node, event->x, event->y); + wlr_xwayland_surface_configure(c->surface.xwayland, + event->x, event->y, event->width, event->height); + return; + } + if ((c->isfloating && c != grabc) || !c->mon->lt[c->mon->sellt]->arrange) + resize(c, (struct wlr_box){.x = event->x - c->bw, .y = event->y - c->bw, + .width = event->width + c->bw * 2, .height = event->height + c->bw * 2}, 0); + else + arrange(c->mon); +} + +/*创建窗口监测函数*/ +void +createnotifyx11(struct wl_listener *listener, void *data) +{ + struct wlr_xwayland_surface *xsurface = data; + Client *c; + + /* Allocate a Client for this surface */ + c = xsurface->data = ecalloc(1, sizeof(*c)); + c->surface.xwayland = xsurface; + c->type = X11; + c->bw = borderpx; + + /* Listen to the various events it can emit */ + LISTEN(&xsurface->events.associate, &c->associate, associatex11); + LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociatex11); + LISTEN(&xsurface->events.request_activate, &c->activate, activatex11); + LISTEN(&xsurface->events.request_configure, &c->configure, configurex11); + LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints); + LISTEN(&xsurface->events.set_title, &c->set_title, updatetitle); + LISTEN(&xsurface->events.destroy, &c->destroy, destroynotify); + LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreennotify); + LISTEN(&xsurface->events.request_maximize, &c->maximize, maximizenotify); + LISTEN(&xsurface->events.request_minimize, &c->minimize, minimizenotify); +} + +Atom +getatom(xcb_connection_t *xc, const char *name) +{ + Atom atom = 0; + xcb_intern_atom_reply_t *reply; + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(xc, 0, strlen(name), name); + if ((reply = xcb_intern_atom_reply(xc, cookie, NULL))) + atom = reply->atom; + free(reply); + + return atom; +} + +void +sethints(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, set_hints); + if (c != focustop(selmon)) { + c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints); + printstatus(); + } +} + +void +xwaylandready(struct wl_listener *listener, void *data) +{ + struct wlr_xcursor *xcursor; + xcb_connection_t *xc = xcb_connect(xwayland->display_name, NULL); + int err = xcb_connection_has_error(xc); + if (err) { + fprintf(stderr, "xcb_connect to X server failed with code %d\n. Continuing with degraded functionality.\n", err); + return; + } + + /* Collect atoms we are interested in. If getatom returns 0, we will + * not detect that window type. */ + netatom[NetWMWindowTypeDialog] = getatom(xc, "_NET_WM_WINDOW_TYPE_DIALOG"); + netatom[NetWMWindowTypeSplash] = getatom(xc, "_NET_WM_WINDOW_TYPE_SPLASH"); + netatom[NetWMWindowTypeToolbar] = getatom(xc, "_NET_WM_WINDOW_TYPE_TOOLBAR"); + netatom[NetWMWindowTypeUtility] = getatom(xc, "_NET_WM_WINDOW_TYPE_UTILITY"); + + /* assign the one and only seat */ + wlr_xwayland_set_seat(xwayland, seat); + + /* Set the default XWayland cursor to match the rest of dwl. */ + if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "left_ptr", 1))) + wlr_xwayland_set_cursor(xwayland, + xcursor->images[0]->buffer, xcursor->images[0]->width * 4, + xcursor->images[0]->width, xcursor->images[0]->height, + xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y); + + xcb_disconnect(xc); +} +#endif + +int +main(int argc, char *argv[]) +{ + char *startup_cmd = NULL; + int c; + + while ((c = getopt(argc, argv, "s:hdv")) != -1) { + if (c == 's') + startup_cmd = optarg; + else if (c == 'd') + log_level = WLR_DEBUG; + else if (c == 'v') + die("dwl " VERSION); + else + goto usage; + } + if (optind < argc) + goto usage; + + /* Wayland requires XDG_RUNTIME_DIR for creating its communications socket */ + if (!getenv("XDG_RUNTIME_DIR")) + die("XDG_RUNTIME_DIR must be set"); + setup(); + run(startup_cmd); + cleanup(); + return EXIT_SUCCESS; + +usage: + die("Usage: %s [-v] [-d] [-s startup command]", argv[0]); +} \ No newline at end of file diff --git a/dwl.desktop b/dwl.desktop new file mode 100644 index 0000000..5ab0498 --- /dev/null +++ b/dwl.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Dwl +Comment=Dynamic window manager for wayland +Exec=env XDG_CURRENT_DESKTOP=sway DWL=/home/user/.config/dwl dwl +Icon=dwl +Type=Application diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..9935d0d --- /dev/null +++ b/meson.build @@ -0,0 +1,50 @@ +project('dwl', ['c', 'cpp'], + version : '0.4' +) + +subdir('protocols') + +cc = meson.get_compiler('c') +libm = cc.find_library('m') +xcb = dependency('xcb', required : get_option('xwayland')) +xlibs = dependency('xcb-icccm', required : get_option('xwayland')) +wayland_server_dep = dependency('wayland-server') +wlroots_dep = dependency('wlroots', version : ['>=0.17.0', '<0.18.0']) +xkbcommon_dep = dependency('xkbcommon') +libinput_dep = dependency('libinput') +libwayland_client_dep = dependency('wayland-client') + +c_args = [ + '-g', + '-Wno-unused-function', + '-DWLR_USE_UNSTABLE', + '-D_POSIX_C_SOURCE=200809L', + '-DVERSION="@0@"'.format(meson.project_version()) +] + +if xcb.found() and xlibs.found() + c_args += '-DXWAYLAND' + c_args += '-DIM' + endif + +executable('dwl', + 'dwl.c', + 'util.c', + 'wlr_foreign_toplevel_management_v1.c', + 'wlr_foreign_toplevel_management_v1.h', + wayland_sources, + dependencies : [ + libm, + xcb, + xlibs, + wayland_server_dep, + wlroots_dep, + xkbcommon_dep, + libinput_dep, + libwayland_client_dep, + ], + install : true, + c_args : c_args +) + +install_man('dwl.1') diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..63b4ede --- /dev/null +++ b/meson_options.txt @@ -0,0 +1 @@ +option('xwayland', type : 'feature', value : 'enabled') diff --git a/protocols/dwl-ipc-unstable-v2.xml b/protocols/dwl-ipc-unstable-v2.xml new file mode 100644 index 0000000..74a212f --- /dev/null +++ b/protocols/dwl-ipc-unstable-v2.xml @@ -0,0 +1,166 @@ + + + + + This protocol allows clients to update and get updates from dwl. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible + changes may be added together with the corresponding interface + version bump. + Backward incompatible changes are done by bumping the version + number in the protocol and interface names and resetting the + interface version. Once the protocol is to be declared stable, + the 'z' prefix and the version number in the protocol and + interface names are removed and the interface version number is + reset. + + + + + This interface is exposed as a global in wl_registry. + + Clients can use this interface to get a dwl_ipc_output. + After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. + The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. + + + + + Indicates that the client will not the dwl_ipc_manager object anymore. + Objects created through this instance are not affected. + + + + + + Get a dwl_ipc_outout for the specified wl_output. + + + + + + + + This event is sent after binding. + A roundtrip after binding guarantees the client recieved all tags. + + + + + + + This event is sent after binding. + A roundtrip after binding guarantees the client recieved all layouts. + + + + + + + + Observe and control a dwl output. + + Events are double-buffered: + Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. + + Request are not double-buffered: + The compositor will update immediately upon request. + + + + + + + + + + + Indicates to that the client no longer needs this dwl_ipc_output. + + + + + + Indicates the client should hide or show themselves. + If the client is visible then hide, if hidden then show. + + + + + + Indicates if the output is active. Zero is invalid, nonzero is valid. + + + + + + + Indicates that a tag has been updated. + + + + + + + + + + Indicates a new layout is selected. + + + + + + + Indicates the title has changed. + + + + + + + Indicates the appid has changed. + + + + + + + Indicates the layout has changed. Since layout symbols are dynamic. + As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. + You can ignore the zdwl_ipc_output.layout event. + + + + + + + Indicates that a sequence of status updates have finished and the client should redraw. + + + + + + + + + + + + The tags are updated as follows: + new_tags = (current_tags AND and_tags) XOR xor_tags + + + + + + + + + + + diff --git a/protocols/meson.build b/protocols/meson.build new file mode 100644 index 0000000..1e3d777 --- /dev/null +++ b/protocols/meson.build @@ -0,0 +1,24 @@ +wayland_scanner = find_program('wayland-scanner') +wayland_protos_dep = dependency('wayland-protocols') +wl_protocol_dir = wayland_protos_dep.get_pkgconfig_variable('pkgdatadir') +wayland_scanner_code = generator( + wayland_scanner, + output: '@BASENAME@-protocol.c', + arguments: ['private-code', '@INPUT@', '@OUTPUT@']) +wayland_scanner_client = generator( + wayland_scanner, + output: '@BASENAME@-protocol.h', + arguments: ['server-header', '@INPUT@', '@OUTPUT@']) + +wayland_xmls = [ + wl_protocol_dir + '/stable/xdg-shell/xdg-shell.xml', + 'wlr-layer-shell-unstable-v1.xml', + 'pointer-constraints-unstable-v1.xml', + 'wlr-foreign-toplevel-management-unstable-v1.xml', + 'dwl-ipc-unstable-v2.xml', + # 'cursor-shape-v1.xml' +] +wayland_sources = [ + wayland_scanner_code.process(wayland_xmls), + wayland_scanner_client.process(wayland_xmls), +] diff --git a/protocols/pointer-constraints-unstable-v1.xml b/protocols/pointer-constraints-unstable-v1.xml new file mode 100755 index 0000000..efd64b6 --- /dev/null +++ b/protocols/pointer-constraints-unstable-v1.xml @@ -0,0 +1,339 @@ + + + + + Copyright © 2014 Jonas Ådahl + Copyright © 2015 Red Hat Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol specifies a set of interfaces used for adding constraints to + the motion of a pointer. Possible constraints include confining pointer + motions to a given region, or locking it to its current position. + + In order to constrain the pointer, a client must first bind the global + interface "wp_pointer_constraints" which, if a compositor supports pointer + constraints, is exposed by the registry. Using the bound global object, the + client uses the request that corresponds to the type of constraint it wants + to make. See wp_pointer_constraints for more details. + + Warning! The protocol described in this file is experimental and backward + incompatible changes may be made. Backward compatible changes may be added + together with the corresponding interface version bump. Backward + incompatible changes are done by bumping the version number in the protocol + and interface names and resetting the interface version. Once the protocol + is to be declared stable, the 'z' prefix and the version number in the + protocol and interface names are removed and the interface version number is + reset. + + + + + The global interface exposing pointer constraining functionality. It + exposes two requests: lock_pointer for locking the pointer to its + position, and confine_pointer for locking the pointer to a region. + + The lock_pointer and confine_pointer requests create the objects + wp_locked_pointer and wp_confined_pointer respectively, and the client can + use these objects to interact with the lock. + + For any surface, only one lock or confinement may be active across all + wl_pointer objects of the same seat. If a lock or confinement is requested + when another lock or confinement is active or requested on the same surface + and with any of the wl_pointer objects of the same seat, an + 'already_constrained' error will be raised. + + + + + These errors can be emitted in response to wp_pointer_constraints + requests. + + + + + + + These values represent different lifetime semantics. They are passed + as arguments to the factory requests to specify how the constraint + lifetimes should be managed. + + + + A oneshot pointer constraint will never reactivate once it has been + deactivated. See the corresponding deactivation event + (wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for + details. + + + + + A persistent pointer constraint may again reactivate once it has + been deactivated. See the corresponding deactivation event + (wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for + details. + + + + + + + Used by the client to notify the server that it will no longer use this + pointer constraints object. + + + + + + The lock_pointer request lets the client request to disable movements of + the virtual pointer (i.e. the cursor), effectively locking the pointer + to a position. This request may not take effect immediately; in the + future, when the compositor deems implementation-specific constraints + are satisfied, the pointer lock will be activated and the compositor + sends a locked event. + + The protocol provides no guarantee that the constraints are ever + satisfied, and does not require the compositor to send an error if the + constraints cannot ever be satisfied. It is thus possible to request a + lock that will never activate. + + There may not be another pointer constraint of any kind requested or + active on the surface for any of the wl_pointer objects of the seat of + the passed pointer when requesting a lock. If there is, an error will be + raised. See general pointer lock documentation for more details. + + The intersection of the region passed with this request and the input + region of the surface is used to determine where the pointer must be + in order for the lock to activate. It is up to the compositor whether to + warp the pointer or require some kind of user interaction for the lock + to activate. If the region is null the surface input region is used. + + A surface may receive pointer focus without the lock being activated. + + The request creates a new object wp_locked_pointer which is used to + interact with the lock as well as receive updates about its state. See + the the description of wp_locked_pointer for further information. + + Note that while a pointer is locked, the wl_pointer objects of the + corresponding seat will not emit any wl_pointer.motion events, but + relative motion events will still be emitted via wp_relative_pointer + objects of the same seat. wl_pointer.axis and wl_pointer.button events + are unaffected. + + + + + + + + + + + The confine_pointer request lets the client request to confine the + pointer cursor to a given region. This request may not take effect + immediately; in the future, when the compositor deems implementation- + specific constraints are satisfied, the pointer confinement will be + activated and the compositor sends a confined event. + + The intersection of the region passed with this request and the input + region of the surface is used to determine where the pointer must be + in order for the confinement to activate. It is up to the compositor + whether to warp the pointer or require some kind of user interaction for + the confinement to activate. If the region is null the surface input + region is used. + + The request will create a new object wp_confined_pointer which is used + to interact with the confinement as well as receive updates about its + state. See the the description of wp_confined_pointer for further + information. + + + + + + + + + + + + The wp_locked_pointer interface represents a locked pointer state. + + While the lock of this object is active, the wl_pointer objects of the + associated seat will not emit any wl_pointer.motion events. + + This object will send the event 'locked' when the lock is activated. + Whenever the lock is activated, it is guaranteed that the locked surface + will already have received pointer focus and that the pointer will be + within the region passed to the request creating this object. + + To unlock the pointer, send the destroy request. This will also destroy + the wp_locked_pointer object. + + If the compositor decides to unlock the pointer the unlocked event is + sent. See wp_locked_pointer.unlock for details. + + When unlocking, the compositor may warp the cursor position to the set + cursor position hint. If it does, it will not result in any relative + motion events emitted via wp_relative_pointer. + + If the surface the lock was requested on is destroyed and the lock is not + yet activated, the wp_locked_pointer object is now defunct and must be + destroyed. + + + + + Destroy the locked pointer object. If applicable, the compositor will + unlock the pointer. + + + + + + Set the cursor position hint relative to the top left corner of the + surface. + + If the client is drawing its own cursor, it should update the position + hint to the position of its own cursor. A compositor may use this + information to warp the pointer upon unlock in order to avoid pointer + jumps. + + The cursor position hint is double buffered. The new hint will only take + effect when the associated surface gets it pending state applied. See + wl_surface.commit for details. + + + + + + + + Set a new region used to lock the pointer. + + The new lock region is double-buffered. The new lock region will + only take effect when the associated surface gets its pending state + applied. See wl_surface.commit for details. + + For details about the lock region, see wp_locked_pointer. + + + + + + + Notification that the pointer lock of the seat's pointer is activated. + + + + + + Notification that the pointer lock of the seat's pointer is no longer + active. If this is a oneshot pointer lock (see + wp_pointer_constraints.lifetime) this object is now defunct and should + be destroyed. If this is a persistent pointer lock (see + wp_pointer_constraints.lifetime) this pointer lock may again + reactivate in the future. + + + + + + + The wp_confined_pointer interface represents a confined pointer state. + + This object will send the event 'confined' when the confinement is + activated. Whenever the confinement is activated, it is guaranteed that + the surface the pointer is confined to will already have received pointer + focus and that the pointer will be within the region passed to the request + creating this object. It is up to the compositor to decide whether this + requires some user interaction and if the pointer will warp to within the + passed region if outside. + + To unconfine the pointer, send the destroy request. This will also destroy + the wp_confined_pointer object. + + If the compositor decides to unconfine the pointer the unconfined event is + sent. The wp_confined_pointer object is at this point defunct and should + be destroyed. + + + + + Destroy the confined pointer object. If applicable, the compositor will + unconfine the pointer. + + + + + + Set a new region used to confine the pointer. + + The new confine region is double-buffered. The new confine region will + only take effect when the associated surface gets its pending state + applied. See wl_surface.commit for details. + + If the confinement is active when the new confinement region is applied + and the pointer ends up outside of newly applied region, the pointer may + warped to a position within the new confinement region. If warped, a + wl_pointer.motion event will be emitted, but no + wp_relative_pointer.relative_motion event. + + The compositor may also, instead of using the new region, unconfine the + pointer. + + For details about the confine region, see wp_confined_pointer. + + + + + + + Notification that the pointer confinement of the seat's pointer is + activated. + + + + + + Notification that the pointer confinement of the seat's pointer is no + longer active. If this is a oneshot pointer confinement (see + wp_pointer_constraints.lifetime) this object is now defunct and should + be destroyed. If this is a persistent pointer confinement (see + wp_pointer_constraints.lifetime) this pointer confinement may again + reactivate in the future. + + + + + diff --git a/protocols/wlr-foreign-toplevel-management-unstable-v1.xml b/protocols/wlr-foreign-toplevel-management-unstable-v1.xml new file mode 100644 index 0000000..1081337 --- /dev/null +++ b/protocols/wlr-foreign-toplevel-management-unstable-v1.xml @@ -0,0 +1,270 @@ + + + + Copyright © 2018 Ilia Bozhinov + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + The purpose of this protocol is to enable the creation of taskbars + and docks by providing them with a list of opened applications and + letting them request certain actions on them, like maximizing, etc. + + After a client binds the zwlr_foreign_toplevel_manager_v1, each opened + toplevel window will be sent via the toplevel event + + + + + This event is emitted whenever a new toplevel window is created. It + is emitted for all toplevels, regardless of the app that has created + them. + + All initial details of the toplevel(title, app_id, states, etc.) will + be sent immediately after this event via the corresponding events in + zwlr_foreign_toplevel_handle_v1. + + + + + + + Indicates the client no longer wishes to receive events for new toplevels. + However the compositor may emit further toplevel_created events, until + the finished event is emitted. + + The client must not send any more requests after this one. + + + + + + This event indicates that the compositor is done sending events to the + zwlr_foreign_toplevel_manager_v1. The server will destroy the object + immediately after sending this request, so it will become invalid and + the client should free any resources associated with it. + + + + + + + A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel + window. Each app may have multiple opened toplevels. + + Each toplevel has a list of outputs it is visible on, conveyed to the + client with the output_enter and output_leave events. + + + + + This event is emitted whenever the title of the toplevel changes. + + + + + + + This event is emitted whenever the app-id of the toplevel changes. + + + + + + + This event is emitted whenever the toplevel becomes visible on + the given output. A toplevel may be visible on multiple outputs. + + + + + + + This event is emitted whenever the toplevel stops being visible on + the given output. It is guaranteed that an entered-output event + with the same output has been emitted before this event. + + + + + + + Requests that the toplevel be maximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unmaximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be minimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unminimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Request that this toplevel be activated on the given seat. + There is no guarantee the toplevel will be actually activated. + + + + + + + The different states that a toplevel can have. These have the same meaning + as the states with the same names defined in xdg-toplevel + + + + + + + + + + + This event is emitted immediately after the zlw_foreign_toplevel_handle_v1 + is created and each time the toplevel state changes, either because of a + compositor action or because of a request in this protocol. + + + + + + + + This event is sent after all changes in the toplevel state have been + sent. + + This allows changes to the zwlr_foreign_toplevel_handle_v1 properties + to be seen as atomic, even if they happen via multiple events. + + + + + + Send a request to the toplevel to close itself. The compositor would + typically use a shell-specific method to carry out this request, for + example by sending the xdg_toplevel.close event. However, this gives + no guarantees the toplevel will actually be destroyed. If and when + this happens, the zwlr_foreign_toplevel_handle_v1.closed event will + be emitted. + + + + + + The rectangle of the surface specified in this request corresponds to + the place where the app using this protocol represents the given toplevel. + It can be used by the compositor as a hint for some operations, e.g + minimizing. The client is however not required to set this, in which + case the compositor is free to decide some default value. + + If the client specifies more than one rectangle, only the last one is + considered. + + The dimensions are given in surface-local coordinates. + Setting width=height=0 removes the already-set rectangle. + + + + + + + + + + + + + + + + This event means the toplevel has been destroyed. It is guaranteed there + won't be any more events for this zwlr_foreign_toplevel_handle_v1. The + toplevel itself becomes inert so any requests will be ignored except the + destroy request. + + + + + + Destroys the zwlr_foreign_toplevel_handle_v1 object. + + This request should be called either when the client does not want to + use the toplevel anymore or after the closed event to finalize the + destruction of the object. + + + + + + + + Requests that the toplevel be fullscreened on the given output. If the + fullscreen state and/or the outputs the toplevel is visible on actually + change, this will be indicated by the state and output_enter/leave + events. + + The output parameter is only a hint to the compositor. Also, if output + is NULL, the compositor should decide which output the toplevel will be + fullscreened on, if at all. + + + + + + + Requests that the toplevel be unfullscreened. If the fullscreen state + actually changes, this will be indicated by the state event. + + + + + + + + This event is emitted whenever the parent of the toplevel changes. + + No event is emitted when the parent handle is destroyed by the client. + + + + + diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 0000000..d62fd51 --- /dev/null +++ b/protocols/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,390 @@ + + + + Copyright © 2017 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Clients can use this interface to assign the surface_layer role to + wl_surfaces. Such surfaces are assigned to a "layer" of the output and + rendered with a defined z-depth respective to each other. They may also be + anchored to the edges and corners of a screen and specify input handling + semantics. This interface should be suitable for the implementation of + many desktop shell components, and a broad number of other applications + that interact with the desktop. + + + + + Create a layer surface for an existing surface. This assigns the role of + layer_surface, or raises a protocol error if another role is already + assigned. + + Creating a layer surface from a wl_surface which has a buffer attached + or committed is a client error, and any attempts by a client to attach + or manipulate a buffer prior to the first layer_surface.configure call + must also be treated as errors. + + After creating a layer_surface object and setting it up, the client + must perform an initial commit without any buffer attached. + The compositor will reply with a layer_surface.configure event. + The client must acknowledge it and is then allowed to attach a buffer + to map the surface. + + You may pass NULL for output to allow the compositor to decide which + output to use. Generally this will be the one that the user most + recently interacted with. + + Clients can specify a namespace that defines the purpose of the layer + surface. + + + + + + + + + + + + + + + + + These values indicate which layers a surface can be rendered in. They + are ordered by z depth, bottom-most first. Traditional shell surfaces + will typically be rendered between the bottom and top layers. + Fullscreen shell surfaces are typically rendered at the top layer. + Multiple surfaces can share a single layer, and ordering within a + single layer is undefined. + + + + + + + + + + + + + This request indicates that the client will not use the layer_shell + object any more. Objects that have been created through this instance + are not affected. + + + + + + + An interface that may be implemented by a wl_surface, for surfaces that + are designed to be rendered as a layer of a stacked desktop-like + environment. + + Layer surface state (layer, size, anchor, exclusive zone, + margin, interactivity) is double-buffered, and will be applied at the + time wl_surface.commit of the corresponding wl_surface is called. + + Attaching a null buffer to a layer surface unmaps it. + + Unmapping a layer_surface means that the surface cannot be shown by the + compositor until it is explicitly mapped again. The layer_surface + returns to the state it had right after layer_shell.get_layer_surface. + The client can re-map the surface by performing a commit without any + buffer attached, waiting for a configure event and handling it as usual. + + + + + Sets the size of the surface in surface-local coordinates. The + compositor will display the surface centered with respect to its + anchors. + + If you pass 0 for either value, the compositor will assign it and + inform you of the assignment in the configure event. You must set your + anchor to opposite edges in the dimensions you omit; not doing so is a + protocol error. Both values are 0 by default. + + Size is double-buffered, see wl_surface.commit. + + + + + + + + Requests that the compositor anchor the surface to the specified edges + and corners. If two orthogonal edges are specified (e.g. 'top' and + 'left'), then the anchor point will be the intersection of the edges + (e.g. the top left corner of the output); otherwise the anchor point + will be centered on that edge, or in the center if none is specified. + + Anchor is double-buffered, see wl_surface.commit. + + + + + + + Requests that the compositor avoids occluding an area with other + surfaces. The compositor's use of this information is + implementation-dependent - do not assume that this region will not + actually be occluded. + + A positive value is only meaningful if the surface is anchored to one + edge or an edge and both perpendicular edges. If the surface is not + anchored, anchored to only two perpendicular edges (a corner), anchored + to only two parallel edges or anchored to all edges, a positive value + will be treated the same as zero. + + A positive zone is the distance from the edge in surface-local + coordinates to consider exclusive. + + Surfaces that do not wish to have an exclusive zone may instead specify + how they should interact with surfaces that do. If set to zero, the + surface indicates that it would like to be moved to avoid occluding + surfaces with a positive exclusive zone. If set to -1, the surface + indicates that it would not like to be moved to accommodate for other + surfaces, and the compositor should extend it all the way to the edges + it is anchored to. + + For example, a panel might set its exclusive zone to 10, so that + maximized shell surfaces are not shown on top of it. A notification + might set its exclusive zone to 0, so that it is moved to avoid + occluding the panel, but shell surfaces are shown underneath it. A + wallpaper or lock screen might set their exclusive zone to -1, so that + they stretch below or over the panel. + + The default value is 0. + + Exclusive zone is double-buffered, see wl_surface.commit. + + + + + + + Requests that the surface be placed some distance away from the anchor + point on the output, in surface-local coordinates. Setting this value + for edges you are not anchored to has no effect. + + The exclusive zone includes the margin. + + Margin is double-buffered, see wl_surface.commit. + + + + + + + + + + Types of keyboard interaction possible for layer shell surfaces. The + rationale for this is twofold: (1) some applications are not interested + in keyboard events and not allowing them to be focused can improve the + desktop experience; (2) some applications will want to take exclusive + keyboard focus. + + + + + This value indicates that this surface is not interested in keyboard + events and the compositor should never assign it the keyboard focus. + + This is the default value, set for newly created layer shell surfaces. + + This is useful for e.g. desktop widgets that display information or + only have interaction with non-keyboard input devices. + + + + + Request exclusive keyboard focus if this surface is above the shell surface layer. + + For the top and overlay layers, the seat will always give + exclusive keyboard focus to the top-most layer which has keyboard + interactivity set to exclusive. If this layer contains multiple + surfaces with keyboard interactivity set to exclusive, the compositor + determines the one receiving keyboard events in an implementation- + defined manner. In this case, no guarantee is made when this surface + will receive keyboard focus (if ever). + + For the bottom and background layers, the compositor is allowed to use + normal focus semantics. + + This setting is mainly intended for applications that need to ensure + they receive all keyboard events, such as a lock screen or a password + prompt. + + + + + This requests the compositor to allow this surface to be focused and + unfocused by the user in an implementation-defined manner. The user + should be able to unfocus this surface even regardless of the layer + it is on. + + Typically, the compositor will want to use its normal mechanism to + manage keyboard focus between layer shell surfaces with this setting + and regular toplevels on the desktop layer (e.g. click to focus). + Nevertheless, it is possible for a compositor to require a special + interaction to focus or unfocus layer shell surfaces (e.g. requiring + a click even if focus follows the mouse normally, or providing a + keybinding to switch focus between layers). + + This setting is mainly intended for desktop shell components (e.g. + panels) that allow keyboard interaction. Using this option can allow + implementing a desktop shell that can be fully usable without the + mouse. + + + + + + + Set how keyboard events are delivered to this surface. By default, + layer shell surfaces do not receive keyboard events; this request can + be used to change this. + + This setting is inherited by child surfaces set by the get_popup + request. + + Layer surfaces receive pointer, touch, and tablet events normally. If + you do not want to receive them, set the input region on your surface + to an empty region. + + Keyboard interactivity is double-buffered, see wl_surface.commit. + + + + + + + This assigns an xdg_popup's parent to this layer_surface. This popup + should have been created via xdg_surface::get_popup with the parent set + to NULL, and this request must be invoked before committing the popup's + initial state. + + See the documentation of xdg_popup for more details about what an + xdg_popup is and how it is used. + + + + + + + When a configure event is received, if a client commits the + surface in response to the configure event, then the client + must make an ack_configure request sometime before the commit + request, passing along the serial of the configure event. + + If the client receives multiple configure events before it + can respond to one, it only has to ack the last configure event. + + A client is not required to commit immediately after sending + an ack_configure request - it may even ack_configure several times + before its next surface commit. + + A client may send multiple ack_configure requests before committing, but + only the last request sent before a commit indicates which configure + event the client really is responding to. + + + + + + + This request destroys the layer surface. + + + + + + The configure event asks the client to resize its surface. + + Clients should arrange their surface for the new states, and then send + an ack_configure request with the serial sent in this configure event at + some point before committing the new surface. + + The client is free to dismiss all but the last configure event it + received. + + The width and height arguments specify the size of the window in + surface-local coordinates. + + The size is a hint, in the sense that the client is free to ignore it if + it doesn't resize, pick a smaller size (to satisfy aspect ratio or + resize in steps of NxM pixels). If the client picks a smaller size and + is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the + surface will be centered on this axis. + + If the width or height arguments are zero, it means the client should + decide its own window dimension. + + + + + + + + + The closed event is sent by the compositor when the surface will no + longer be shown. The output may have been destroyed or the user may + have asked for it to be removed. Further changes to the surface will be + ignored. The client should destroy the resource after receiving this + event, and create a new surface if they so choose. + + + + + + + + + + + + + + + + + + + + + + Change the layer that the surface is rendered on. + + Layer is double-buffered, see wl_surface.commit. + + + + + diff --git a/util.c b/util.c new file mode 100644 index 0000000..cca7c19 --- /dev/null +++ b/util.c @@ -0,0 +1,35 @@ +/* See LICENSE.dwm file for copyright and license details. */ +#include +#include +#include +#include + +#include "util.h" + +void +die(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..4c94117 --- /dev/null +++ b/util.h @@ -0,0 +1,4 @@ +/* See LICENSE.dwm file for copyright and license details. */ + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); diff --git a/waybar/config b/waybar/config new file mode 100755 index 0000000..009c432 --- /dev/null +++ b/waybar/config @@ -0,0 +1,163 @@ +{ + "layer": "bottom", + "position": "top", + "height": 62, + "spacing": 5, + + "margin-bottom":-11, + "modules-left": ["dwl/tags","wlr/taskbar","dwl/window"], + "modules-right": ["custom/notification","tray","network","cpu","temperature","memory","clock","pulseaudio","backlight","battery"], + "dwl/tags": { + "num-tags":9, + "hide-unused-tag":true + }, + "custom/notification": { + "tooltip": false, + "format": "{icon}", + "format-icons": { + "notification": "", + "none": "", + "dnd-notification": "", + "dnd-none": "", + "inhibited-notification": "", + "inhibited-none": "", + "dnd-inhibited-notification": "", + "dnd-inhibited-none": "" + }, + "return-type": "json", + "exec-if": "which swaync-client", + "exec": "swaync-client -swb", + "on-click": "sleep 0.1s && swaync-client -t -sw", + "on-click-right": "swaync-client -d -sw", + "escape": true + }, + "wlr/taskbar": { + "format": "{icon}", + "icon-size": 24, + "all-outputs": false, + "tooltip-format": "{title}", + "markup": true, + "on-click": "activate", + "on-click-right": "close", + "ignore-list": ["Rofi","wofi"] + }, + "pulseaudio": { + "interval":1, + "scroll-step": 1, + "format": "{volume}%{icon}", + "format-bluetooth": "{volume}%", + "format-bluetooth-muted": "{volume}%", + "format-muted": "{volume}%{icon}", + "format-icons": { + "default": ["", "", ""] + }, + "on-click": "blueman-manager", + "on-click-right":"amixer set Master toggle", + }, + "keyboard-state": { + "interval":1, + "numlock": true, + "capslock": true, + "format": "{name} {icon}", + "format-icons": { + "locked": "", + "unlocked": "" + } + }, + "tray": { + "interval":1, + "icon-size": 21, + "spacing": 10 + }, + "clock": { + //"format": "{:%I:%M %p}", + "format": "{:%H:%M} " , + "format-alt": "{:%A, %b %d} ", + "tooltip-format": "{:%Y}", + // "tooltip-format": "{calendar}", + "on-click-right": "gnome-calendar", + "calendar": { + "mode" : "year", + "mode-mon-col" : 3, + "weeks-pos" : "right", + "on-scroll" : 1, + "on-click-right": "gnome-calendar", + "format": { + "months": "{}", + "days": "{}", + "weeks": "W{}", + "weekdays": "{}", + "today": "{}" + } + } + }, + "cpu": { + "interval": 2, + "format": "{load}% ", + "on-click-right":"gnome-system-monitor", + "on-click":"nwg-drawer -ovl" + // "on-click-right":"bash ~/.config/eww/scripts/launcher toggle_menu quicksettings", + // "max-length": 10 + }, + "memory": { + "interval":2, + "format": "{}% 󰍛", + "on-click":"GDK_BACKEND=x11 nemo", + "on-click-right":"cliphist list | rofi -dmenu -normal-window | cliphist decode | wl-copy" + // "on-click-right":"clipman pick -t wofi" + }, + "temperature": { + "thermal-zone": 2, + "hwmon-path": "/sys/class/hwmon/hwmon1/temp1_input", + "critical-threshold": 10, + "format-critical": "{temperatureC}°C", + "format": "", + "on-click":"rofi -normal-window -theme ~/.config/rofi/themes/fancy2.rasi -modi blocks -show blocks -blocks-wrap ~/.config/rofi/history.py", + "on-click-right":"rofi -normal-window -theme ~/.config/rofi/themes/fancy2.rasi -modi blocks -show blocks -blocks-wrap ~/.config/rofi/recentfile.py" + // "on-click":"$DWL/wofi/history_wofi.py", + // "on-click-right":"$DWL/wofi/recentfile_wofi.py" + }, + "backlight": { + "interval":2, + "device": "amdgpu_bl0", + "format": "{percent}% {icon}", + "format-icons": ["󰖔","󰖨"], + "on-scroll-up":"~/.config/hypr/scripts/lightup", + "on-scroll-down":"~/.config/hypr/scripts/lightdown", + "smooth-scrolling-threshold":1 + }, + "battery": { + "bat": "BAT0", + "interval": 60, + "states": { + "warning": 30, + "critical": 10 + }, + "format": "{capacity}% {icon}", + "format-icons": ["", "", "", "", ""], + "on-click-right":"~/.config/eww/System-Menu/launch", + "on-click":"wlogout --protocol layer-shell -b 6" + // "on-click":"/usr/local/bin/eww open-many --toggle background-closer powermenu" + // "max-length": 25 + }, + "disk": { + "interval": 30, + //"format": "󰋊", + "path": "/", + //"format-alt-click": "click", + "format": "{percentage_used}% ", + "on-click":"nemo", + //"tooltip": true, + "tooltip-format": "{used} used out of {total} on {path} ({percentage_used}%)", + }, + "network": { + "interval": 2, + "format-wifi": "{essid} ({signalStrength}%)\uf1eb", + "format-ethernet": "󰈀 {ifname}", + "format-linked": "\uf059 No IP ({ifname})", + "format-disconnected": "\uf071 Disconnected", + "tooltip-format": "{ifname} {ipaddr}/{cidr} via {gwaddr}", + "format-alt": "\uf1eb↓{bandwidthDownBytes} ↑{bandwidthUpBytes}", + "on-click-right":"~/.config/dwl/rofi/networkmanager_dmenu" + } +} diff --git a/waybar/style.css b/waybar/style.css new file mode 100755 index 0000000..d0e66d9 --- /dev/null +++ b/waybar/style.css @@ -0,0 +1,663 @@ +* { + font-family: CartographCF Nerd Font,FontAwesome, Roboto, Helvetica, Arial, sans-serif; + font-size: 20px; +} + +tooltip { + background: #3C3A38; + border: 2px solid #85796F; + border-radius: 10px; +} + +tooltip label { + color: #EBDBB2; +} + +window#waybar { + background-color: transparent; +} + +#tags{ + background-color: transparent; + margin-top: 14px; + margin-bottom: 14px; + margin-right: 1px; + margin-left: 10px; +} + +#tags button{ + box-shadow: rgba(0, 0, 0, 0.5) 0 -3 5 5px; + background-color: #fff ; + padding-bottom: 1px; + padding-top: 1px; + margin-right: 8px; + padding-right: 7px; + padding-left: 7px; + background-size: 300% 300%; + font-weight: bolder; + color: #a585cd ; + border: none; + border-bottom: 6px solid #d6cdcd; + border-radius: 15px; +} + + + +#tags button.occupied{ + box-shadow: rgba(0, 0, 0, 0.5) 0 -3 5 5px; + background-color: #fff ; + padding-bottom: 1px; + padding-top: 1px; + margin-right: 8px; + padding-right: 7px; + padding-left: 7px; + background-size: 300% 300%; + font-weight: bolder; + color: #a585cd ; + border: none; + border-bottom: 6px solid #d6cdcd; + border-radius: 15px; +} + +#tags button.focused{ + box-shadow: rgba(0, 0, 0, 0.5) 0 -3 5 5px; + background-color: rgb(186, 142, 213); + padding-bottom: 1px; + padding-top: 1px; + margin-right: 8px; + padding-right: 15px; + padding-left: 15px; + background-size: 300% 300%; + font-weight: bolder; + color: #fff ; + border: none; + border-bottom: 6px solid rgb(143, 102, 168); + border-radius: 15px; +} + +#tags button.urgent{ + box-shadow: rgba(0, 0, 0, 0.5) 0 -3 5 5px; + padding-bottom: 1px; + padding-top: 1px; + margin-right: 8px; + padding-right: 7px; + padding-left: 7px; + padding-bottom: 3px; + background: rgb(171, 101, 101); + /* background: linear-gradient(45deg, rgba(202,158,230,1) 0%, rgba(245,194,231,1) 43%, rgba(180,190,254,1) 80%, rgba(137,180,250,1) 100%); */ + background-size: 300% 300%; + /* animation: gradient 10s ease infinite; */ + color: #fff; + border: none; + border-bottom: 6px solid rgb(105, 57, 57); + border-radius: 15px; +} + +#taskbar{ + background-color: transparent; + margin-top: 12px; + margin-bottom: 12px; + margin-right: 8px; + margin-left: 1px; + +} + +#taskbar button{ + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + /* text-shadow: 0 0 2px rgba(0, 0, 0, 0.8); */ + background-color: rgb(237, 196, 147); + margin-right: 8px; + padding-top: 4px; + padding-bottom: 2px; + padding-right: 10px; + padding-left: 10px; + font-weight: bolder; + color: #ededed ; + border: none; + border-bottom: 6px solid rgb(193, 146, 103); + border-radius: 15px; +} + +#taskbar button.minimized{ + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + background-color: rgb(146, 140, 151); + margin-right: 8px; + padding-top: 4px; + padding-bottom: 2px; + padding-right: 10px; + padding-left: 10px; + font-weight: bolder; + color: #cba6f7 ; + border: none; + border-bottom: 6px solid rgb(98, 97, 99); + border-radius: 15px; +} + +#taskbar button.urgent{ + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + background-color: rgb(238, 92, 92); + margin-right: 8px; + padding-top: 4px; + padding-bottom: 2px; + padding-right: 10px; + padding-left: 10px; + font-weight: bolder; + color: #cba6f7 ; + border: none; + border-bottom: 6px solid rgb(183, 63, 63); + border-radius: 15px; +} + +#taskbar button.active{ + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + background-color: rgb(186, 238, 225); + margin-right: 8px; + padding-top: 4px; + padding-bottom: 2px; + padding-right: 10px; + padding-left: 10px; + font-weight: bolder; + color: #cba6f7 ; + border: none; + border-bottom: 6px solid rgb(131, 184, 171); + border-radius: 15px; +} + +window#waybar.empty #window { + background-color: transparent; + color: transparent; + border-bottom:none; + box-shadow: none; + padding-right: 0px; + padding-left: 0px; + margin-left: 0px; + margin-right: 0px; +} + +#window{ + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + /* text-shadow: 0 0 2px rgba(0, 0, 0, 0.8); */ + background-color: rgb(237, 196, 147); + margin-top: 12px; + margin-bottom: 12px; + margin-right: 8px; + padding-top: 2px; + padding-bottom: 2px; + padding-right: 10px; + padding-left: 10px; + font-weight: bolder; + color: rgb(63, 37, 5); + border: none; + border-bottom: 6px solid rgb(193, 146, 103); + border-radius: 15px; +} + + +#custom-notification { + box-shadow: rgba(0, 0, 0, 0.5) 0 -3 5 5px; + background-color: #cb7cc6 ; + padding-bottom: 1px; + padding-top: 1px; + padding-right: 10px; + padding-left: 10px; + background-size: 300% 300%; + font-weight: bolder; + color: rgb(244, 240, 233) ; + border: none; + border-bottom: 6px solid #904e8b; + border-radius: 15px; + margin-top: 10px; + margin-bottom: 10px; + margin-right: 8px; + margin-left: 10px; +} + +@keyframes gradient { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} + + +#clock, +#battery, +#cpu, +#memory, +#disk, +#temperature, +#backlight, +#network, +#pulseaudio, +#custom-media, +#tray, +#mode, +#idle_inhibitor, +#custom-expand, +#custom-cycle_wall, +#custom-ss, +#custom-dynamic_pill, +#mpd { + padding: 0 10px; + border-radius: 15px; + background-color: #cdd6f4; + color: #516079; + box-shadow: rgba(0, 0, 0, 0.116) 2 2 5 2px; + margin-top: 12px; + margin-bottom: 12px; + margin-right: 3px; +} + +#custom-dynamic_pill.low{ + background: rgb(148,226,213); + background: linear-gradient(52deg, rgba(148,226,213,1) 0%, rgba(137,220,235,1) 19%, rgba(116,199,236,1) 43%, rgba(137,180,250,1) 56%, rgba(180,190,254,1) 80%, rgba(186,187,241,1) 100%); + background-size: 300% 300%; + /* text-shadow: 0 0 5px rgba(0, 0, 0, 0.377); */ + animation: gradient 15s ease infinite; + font-weight: bolder; + color: #fff; +} +#custom-dynamic_pill.normal{ + background: rgb(166,209,137); + background: linear-gradient(52deg, rgba(166,209,137,1) 0%, rgba(166,227,161,1) 26%, rgba(148,226,213,1) 65%, rgba(129,200,190,1) 100%); + background-size: 300% 300%; + animation: gradient 15s ease infinite; + /* text-shadow: 0 0 5px rgba(0, 0, 0, 0.377); */ + font-weight: bolder; + color: #fff; +} +#custom-dynamic_pill.critical{ + background: rgb(235,160,172); + background: linear-gradient(52deg, rgba(235,160,172,1) 0%, rgba(243,139,168,1) 30%, rgba(231,130,132,1) 48%, rgba(250,179,135,1) 77%, rgba(249,226,175,1) 100%); + background-size: 300% 300%; + animation: gradient 15s ease infinite; + /* text-shadow: 0 0 5px rgba(0, 0, 0, 0.377); */ + font-weight: bolder; + color: #fff; +} + + +#custom-dynamic_pill.playing{ + background: rgb(249,226,175); + background: linear-gradient(45deg, rgba(249,226,175,1) 0%, rgba(245,194,231,1) 20%, rgba(180,190,254,1) 100%); + background-size: 300% 300%; + animation: gradient 15s ease infinite; + /* text-shadow: 0 0 5px rgba(0, 0, 0, 0.377); */ + font-weight: 900; + color: #fff ; +} +#custom-dynamic_pill.paused{ + background: #fff ; + font-weight: bolder; + color: #b4befe; +} + +#custom-ss{ + background: #fff; + color: rgba(203,166,247,1); + font-weight: bolder; + padding: 5px; + padding-left: 20px; + padding-right: 20px; + border-radius: 15px; +} + +#custom-cycle_wall{ + background: rgb(245,194,231); + /* background: linear-gradient(45deg, rgba(245,194,231,1) 0%, rgba(203,166,247,1) 0%, rgba(243,139,168,1) 13%, rgba(235,160,172,1) 26%, rgba(250,179,135,1) 34%, rgba(249,226,175,1) 49%, rgba(166,227,161,1) 65%, rgba(148,226,213,1) 77%, rgba(137,220,235,1) 82%, rgba(116,199,236,1) 88%, rgba(137,180,250,1) 95%); */ + color: #fff; + background-size: 500% 500%; + /* animation: gradient 7s linear infinite; */ + font-weight: bolder; + padding: 5px; + border-radius: 15px; +} + + +#clock { + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + background: rgb(179, 42, 142); + /* background: linear-gradient(45deg, rgb(99, 6, 74) 0%, rgba(203,166,247,1) 64%, rgba(202,158,230,1) 100%); */ + margin-left: 1px; + margin-right: 3px; + min-width: 50px; + color: #f8e1b7 ; + background-size: 300% 300%; + /* animation: gradient 20s ease infinite; */ + font-size: 20px; + padding-top: 3px; + padding-bottom: 3px; + padding-right: 15px; + font-weight: bolder; + padding-left: 15px; + border: none; + border-bottom: 7px solid rgb(155, 32, 122); + border-radius: 15px; +} + +#battery.charging, #battery.plugged { + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + padding-top: 3px; + padding-bottom: 3px; + min-width: 70px; + color: #bdf8da; + background: #495026 ; + margin-right: 10px; + border: none; + border-bottom: 7px solid #3a401d; + border-radius: 15px; +} + +#battery { + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + background-color: #07683e; + color:#eae4d0; + font-weight: bolder; + font-size: 20px; + padding-top: 3px; + padding-bottom: 3px; + padding-left: 15px; + padding-right: 15px; + margin-right: 10px; + border: none; + border-bottom: 7px solid #297f5a; + border-radius: 15px; +} + +@keyframes blink { + to { + background-color: #f9e2af; + color:#96804e; + } +} + +#battery.critical:not(.charging) { + background-color: #f38ba8; + color:#bf5673; + animation-name: blink; + animation-duration: 0.5s; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-direction: alternate; +} + +label:focus { + +} + +#cpu { + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + min-width: 80px; + margin-right: 3px; + margin-left: 1px; + padding-top: 3px; + padding-bottom: 3px; + background: rgb(39, 154, 207); + /* background: linear-gradient(52deg, rgba(180,190,254,1) 0%, rgba(137,220,235,1) 32%, rgba(137,180,250,1) 72%, rgba(166,227,161,1) 100%); */ + background-size: 300% 300%; + /* animation: gradient 20s ease infinite; */ + /* text-shadow: 0 10 5px rgba(0, 0, 0, 0.8); */ + /* background-color: #b4befe; */ + color: #edebe6; + font-weight: bolder; + border: none; + border-bottom: 7px solid rgb(58, 125, 156); + border-radius: 15px; + + +} + +#memory { + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + margin-right: 3px; + margin-left: 1px; + padding-top: 3px; + padding-bottom: 3px; + min-width: 60px; + background-color: #cba6f7; + color: #55406e; + font-weight: bolder; + border: none; + border-bottom: 7px solid #9172b6; + border-radius: 15px; + +} + +#disk { + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + margin-right: 3px; + margin-left: 1px; + background-color: #cca45a; + color: #572e05; +} + +#backlight { + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + font-weight: bolder; + margin-right: 3px; + margin-left: 1px; + padding-top: 3px; + padding-bottom: 3px; + color: #090427; + min-width: 40px; + background-color: #c3cac2 ; + border: none; + border-bottom: 7px solid #8a9488; + border-radius: 15px; +} + +#network{ + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + font-weight: bolder; + margin-right: 3px; + margin-left: 1px; + padding-top: 3px; + padding-bottom: 3px; + color:#000; +} + +#network.disabled{ + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + margin-right: 3px; + margin-left: 1px; + padding-top: 3px; + padding-bottom: 3px; + background-color: #45475a; +} + +#network.disconnected{ + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + margin-right: 3px; + margin-left: 1px; + padding-top: 3px; + padding-bottom: 3px; + background: rgb(243,139,168); + /* background: linear-gradient(45deg, rgba(243,139,168,1) 0%, rgba(250,179,135,1) 100%); */ + color: #fff; + font-weight: bolder; + padding-right: 11px; + border: none; + border-bottom: 7px solid rgb(213, 115, 142); + border-radius: 15px; +} + +#network.linked, #network.wifi{ + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + margin-right: 3px; + margin-left: 1px; + padding-top: 3px; + padding-bottom: 3px; + background-color: #a6e3a1 ; + border: none; + border-bottom: 7px solid #79b574; + border-radius: 15px; +} + +#network.ethernet{ + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + margin-right: 3px; + margin-left: 1px; + padding-top: 3px; + padding-bottom: 3px; + background-color: #a6e3a1 ; + border: none; + border-bottom: 7px solid #79b574; + border-radius: 15px; +} + + +#pulseaudio { + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + font-weight: bolder; + margin-right: 3px; + margin-left: 1px; + padding-top: 3px; + padding-bottom: 3px; + min-width: 40px; + background-color: #fab387; + color: #734a31; + border: none; + border-bottom: 7px solid #b87c57; + border-radius: 15px; +} + +#pulseaudio.muted { + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + margin-right: 3px; + margin-left: 1px; + padding-top: 3px; + padding-bottom: 3px; + background-color: #9f7154; +} + +#custom-media { + margin-right: 3px; + margin-left: 1px; + color: #66cc99; +} + +#custom-media.custom-spotify { + margin-right: 3px; + margin-left: 1px; + background-color: #66cc99; +} + +#custom-media.custom-vlc { + margin-right: 3px; + margin-left: 1px; + background-color: #ffa000; +} + +#temperature { + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + font-weight: bolder; + margin-right: 3px; + margin-left: 1px; + padding-top: 3px; + padding-bottom: 3px; + background-color: #f9e2af; + color:#96804e; +} +#temperature.critical { + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + font-weight: bolder; + margin-right: 3px; + margin-left: 1px; + padding-top: 3px; + padding-bottom: 3px; + background-color: #ce91a0 ; + color:#50393f; + border: none; + border-bottom: 7px solid #a26a79; + border-radius: 15px; +} + +#tray { + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + margin-right: 3px; + margin-left: 8px; + padding-top: 3px; + padding-bottom: 3px; + background-color: #96d5dd; + border: none; + border-bottom: 7px solid #66a1a9; + border-radius: 15px; +} + +#tray > .passive { + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + margin-right: 3px; + margin-left: 1px; + padding-top: 3px; + padding-bottom: 3px; + -gtk-icon-effect: dim; +} + +#tray > .needs-attention { + box-shadow: rgba(0, 0, 0, 0.5) 0 -6 3 1px; + margin-right: 3px; + margin-left: 1px; + padding-top: 3px; + padding-bottom: 3px; + -gtk-icon-effect: highlight; + background-color: #eb4d4b; +} + +#idle_inhibitor { + margin-right: 3px; + margin-left: 1px; + background-color: #2d3436; +} + +#idle_inhibitor.activated { + margin-right: 3px; + margin-left: 1px; + background-color: #ecf0f1; + color: #2d3436; +} + +#mpd { + background-color: #66cc99; + color: #2a5c45; +} + +#mpd.disconnected { + background-color: #f53c3c; +} + +#mpd.stopped { + background-color: #90b1b1; +} + +#mpd.paused { + background-color: #51a37a; +} + +#language { + background: #00b093; + color: #740864; + padding: 0 5px; + margin: 0 5px; + min-width: 16px; +} + +#keyboard-state { + background: #97e1ad; + color: #000000; + padding: 0 0px; + margin: 0 5px; + min-width: 16px; +} + +#keyboard-state > label { + padding: 0 5px; +} + +#keyboard-state > label.locked { + background: rgba(0, 0, 0, 0.2); +} diff --git a/wlr_foreign_toplevel_management_v1.c b/wlr_foreign_toplevel_management_v1.c new file mode 100755 index 0000000..46c1ae7 --- /dev/null +++ b/wlr_foreign_toplevel_management_v1.c @@ -0,0 +1,712 @@ +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include +#include +#include +#include +#include "wlr-foreign-toplevel-management-unstable-v1-protocol.h" + +#define FOREIGN_TOPLEVEL_MANAGEMENT_V1_VERSION 3 + +static const struct zwlr_foreign_toplevel_handle_v1_interface toplevel_handle_impl; + +static struct wlr_foreign_toplevel_handle_v1 *toplevel_handle_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwlr_foreign_toplevel_handle_v1_interface, + &toplevel_handle_impl)); + return wl_resource_get_user_data(resource); +} + +static void toplevel_handle_send_maximized_event(struct wl_resource *resource, + bool state) { + struct wlr_foreign_toplevel_handle_v1 *toplevel = + toplevel_handle_from_resource(resource); + if (!toplevel) { + return; + } + struct wlr_foreign_toplevel_handle_v1_maximized_event event = { + .toplevel = toplevel, + .maximized = state, + }; + wl_signal_emit_mutable(&toplevel->events.request_maximize, &event); +} + +static void foreign_toplevel_handle_set_maximized(struct wl_client *client, + struct wl_resource *resource) { + toplevel_handle_send_maximized_event(resource, true); +} + +static void foreign_toplevel_handle_unset_maximized(struct wl_client *client, + struct wl_resource *resource) { + toplevel_handle_send_maximized_event(resource, false); +} + +static void toplevel_send_minimized_event(struct wl_resource *resource, + bool state) { + struct wlr_foreign_toplevel_handle_v1 *toplevel = + toplevel_handle_from_resource(resource); + if (!toplevel) { + return; + } + + struct wlr_foreign_toplevel_handle_v1_minimized_event event = { + .toplevel = toplevel, + .minimized = state, + }; + wl_signal_emit_mutable(&toplevel->events.request_minimize, &event); +} + +static void foreign_toplevel_handle_set_minimized(struct wl_client *client, + struct wl_resource *resource) { + toplevel_send_minimized_event(resource, true); +} + +static void foreign_toplevel_handle_unset_minimized(struct wl_client *client, + struct wl_resource *resource) { + toplevel_send_minimized_event(resource, false); +} + +static void toplevel_send_fullscreen_event(struct wl_resource *resource, + bool state, struct wl_resource *output_resource) { + struct wlr_foreign_toplevel_handle_v1 *toplevel = + toplevel_handle_from_resource(resource); + if (!toplevel) { + return; + } + + struct wlr_output *output = NULL; + if (output_resource) { + output = wlr_output_from_resource(output_resource); + } + struct wlr_foreign_toplevel_handle_v1_fullscreen_event event = { + .toplevel = toplevel, + .fullscreen = state, + .output = output, + }; + wl_signal_emit_mutable(&toplevel->events.request_fullscreen, &event); +} + +static void foreign_toplevel_handle_set_fullscreen(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *output) { + toplevel_send_fullscreen_event(resource, true, output); +} + +static void foreign_toplevel_handle_unset_fullscreen(struct wl_client *client, + struct wl_resource *resource) { + toplevel_send_fullscreen_event(resource, false, NULL); +} + +static void foreign_toplevel_handle_activate(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat) { + struct wlr_foreign_toplevel_handle_v1 *toplevel = + toplevel_handle_from_resource(resource); + if (!toplevel) { + return; + } + struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat); + if (!seat_client) { + return; + } + + struct wlr_foreign_toplevel_handle_v1_activated_event event = { + .toplevel = toplevel, + .seat = seat_client->seat, + }; + wl_signal_emit_mutable(&toplevel->events.request_activate, &event); +} + +static void foreign_toplevel_handle_close(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_foreign_toplevel_handle_v1 *toplevel = + toplevel_handle_from_resource(resource); + if (!toplevel) { + return; + } + wl_signal_emit_mutable(&toplevel->events.request_close, toplevel); +} + +static void foreign_toplevel_handle_set_rectangle(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *surface, + int32_t x, int32_t y, int32_t width, int32_t height) { + struct wlr_foreign_toplevel_handle_v1 *toplevel = + toplevel_handle_from_resource(resource); + if (!toplevel) { + return; + } + + if (width < 0 || height < 0) { + wl_resource_post_error(resource, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_INVALID_RECTANGLE, + "invalid rectangle passed to set_rectangle: width/height < 0"); + return; + } + + struct wlr_foreign_toplevel_handle_v1_set_rectangle_event event = { + .toplevel = toplevel, + .surface = wlr_surface_from_resource(surface), + .x = x, + .y = y, + .width = width, + .height = height, + }; + wl_signal_emit_mutable(&toplevel->events.set_rectangle, &event); +} + +static void foreign_toplevel_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct zwlr_foreign_toplevel_handle_v1_interface toplevel_handle_impl = { + .set_maximized = foreign_toplevel_handle_set_maximized, + .unset_maximized = foreign_toplevel_handle_unset_maximized, + .set_minimized = foreign_toplevel_handle_set_minimized, + .unset_minimized = foreign_toplevel_handle_unset_minimized, + .activate = foreign_toplevel_handle_activate, + .close = foreign_toplevel_handle_close, + .set_rectangle = foreign_toplevel_handle_set_rectangle, + .destroy = foreign_toplevel_handle_destroy, + .set_fullscreen = foreign_toplevel_handle_set_fullscreen, + .unset_fullscreen = foreign_toplevel_handle_unset_fullscreen, +}; + +static void toplevel_idle_send_done(void *data) { + struct wlr_foreign_toplevel_handle_v1 *toplevel = data; + struct wl_resource *resource; + wl_resource_for_each(resource, &toplevel->resources) { + zwlr_foreign_toplevel_handle_v1_send_done(resource); + } + + toplevel->idle_source = NULL; +} + +static void toplevel_update_idle_source( + struct wlr_foreign_toplevel_handle_v1 *toplevel) { + if (toplevel->idle_source) { + return; + } + + toplevel->idle_source = wl_event_loop_add_idle(toplevel->manager->event_loop, + toplevel_idle_send_done, toplevel); +} + +void wlr_foreign_toplevel_handle_v1_set_title( + struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *title) { + free(toplevel->title); + toplevel->title = strdup(title); + if (toplevel->title == NULL) { + wlr_log(WLR_ERROR, "failed to allocate memory for toplevel title"); + return; + } + + struct wl_resource *resource; + wl_resource_for_each(resource, &toplevel->resources) { + zwlr_foreign_toplevel_handle_v1_send_title(resource, title); + } + + toplevel_update_idle_source(toplevel); +} + +void wlr_foreign_toplevel_handle_v1_set_app_id( + struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *app_id) { + free(toplevel->app_id); + toplevel->app_id = strdup(app_id); + if (toplevel->app_id == NULL) { + wlr_log(WLR_ERROR, "failed to allocate memory for toplevel app_id"); + return; + } + + struct wl_resource *resource; + wl_resource_for_each(resource, &toplevel->resources) { + zwlr_foreign_toplevel_handle_v1_send_app_id(resource, app_id); + } + + toplevel_update_idle_source(toplevel); +} + +static void send_output_to_resource(struct wl_resource *resource, + struct wlr_output *output, bool enter) { + struct wl_client *client = wl_resource_get_client(resource); + struct wl_resource *output_resource; + + wl_resource_for_each(output_resource, &output->resources) { + if (wl_resource_get_client(output_resource) == client) { + if (enter) { + zwlr_foreign_toplevel_handle_v1_send_output_enter(resource, + output_resource); + } else { + zwlr_foreign_toplevel_handle_v1_send_output_leave(resource, + output_resource); + } + } + } +} + +static void toplevel_send_output(struct wlr_foreign_toplevel_handle_v1 *toplevel, + struct wlr_output *output, bool enter) { + struct wl_resource *resource; + wl_resource_for_each(resource, &toplevel->resources) { + send_output_to_resource(resource, output, enter); + } + + toplevel_update_idle_source(toplevel); +} + +static void toplevel_handle_output_bind(struct wl_listener *listener, + void *data) { + struct wlr_foreign_toplevel_handle_v1_output *toplevel_output = + wl_container_of(listener, toplevel_output, output_bind); + struct wlr_output_event_bind *event = data; + struct wl_client *client = wl_resource_get_client(event->resource); + + struct wl_resource *resource; + wl_resource_for_each(resource, &toplevel_output->toplevel->resources) { + if (wl_resource_get_client(resource) == client) { + send_output_to_resource(resource, toplevel_output->output, true); + } + } + + toplevel_update_idle_source(toplevel_output->toplevel); +} + +static void toplevel_handle_output_destroy(struct wl_listener *listener, + void *data) { + struct wlr_foreign_toplevel_handle_v1_output *toplevel_output = + wl_container_of(listener, toplevel_output, output_destroy); + wlr_foreign_toplevel_handle_v1_output_leave(toplevel_output->toplevel, + toplevel_output->output); +} + +void wlr_foreign_toplevel_handle_v1_output_enter( + struct wlr_foreign_toplevel_handle_v1 *toplevel, + struct wlr_output *output) { + struct wlr_foreign_toplevel_handle_v1_output *toplevel_output; + wl_list_for_each(toplevel_output, &toplevel->outputs, link) { + if (toplevel_output->output == output) { + return; // we have already sent output_enter event + } + } + + toplevel_output = + calloc(1, sizeof(struct wlr_foreign_toplevel_handle_v1_output)); + if (!toplevel_output) { + wlr_log(WLR_ERROR, "failed to allocate memory for toplevel output"); + return; + } + + toplevel_output->output = output; + toplevel_output->toplevel = toplevel; + wl_list_insert(&toplevel->outputs, &toplevel_output->link); + + toplevel_output->output_bind.notify = toplevel_handle_output_bind; + wl_signal_add(&output->events.bind, &toplevel_output->output_bind); + + toplevel_output->output_destroy.notify = toplevel_handle_output_destroy; + wl_signal_add(&output->events.destroy, &toplevel_output->output_destroy); + + toplevel_send_output(toplevel, output, true); +} + +static void toplevel_output_destroy( + struct wlr_foreign_toplevel_handle_v1_output *toplevel_output) { + wl_list_remove(&toplevel_output->link); + wl_list_remove(&toplevel_output->output_bind.link); + wl_list_remove(&toplevel_output->output_destroy.link); + free(toplevel_output); +} + +void wlr_foreign_toplevel_handle_v1_output_leave( + struct wlr_foreign_toplevel_handle_v1 *toplevel, + struct wlr_output *output) { + struct wlr_foreign_toplevel_handle_v1_output *toplevel_output_iterator; + struct wlr_foreign_toplevel_handle_v1_output *toplevel_output = NULL; + + wl_list_for_each(toplevel_output_iterator, &toplevel->outputs, link) { + if (toplevel_output_iterator->output == output) { + toplevel_output = toplevel_output_iterator; + break; + } + } + + if (toplevel_output) { + toplevel_send_output(toplevel, output, false); + toplevel_output_destroy(toplevel_output); + } else { + // XXX: log an error? crash? + } +} + +static bool fill_array_from_toplevel_state(struct wl_array *array, + uint32_t state) { + if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED) { + uint32_t *index = wl_array_add(array, sizeof(uint32_t)); + if (index == NULL) { + return false; + } + *index = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED; + } + if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED) { + uint32_t *index = wl_array_add(array, sizeof(uint32_t)); + if (index == NULL) { + return false; + } + *index = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED; + } + if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) { + uint32_t *index = wl_array_add(array, sizeof(uint32_t)); + if (index == NULL) { + return false; + } + *index = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED; + } + if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) { + uint32_t *index = wl_array_add(array, sizeof(uint32_t)); + if (index == NULL) { + return false; + } + *index = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN; + } + + return true; +} + +static void toplevel_send_state(struct wlr_foreign_toplevel_handle_v1 *toplevel) { + struct wl_array states; + wl_array_init(&states); + bool r = fill_array_from_toplevel_state(&states, toplevel->state); + if (!r) { + struct wl_resource *resource; + wl_resource_for_each(resource, &toplevel->resources) { + wl_resource_post_no_memory(resource); + } + + wl_array_release(&states); + return; + } + + struct wl_resource *resource; + wl_resource_for_each(resource, &toplevel->resources) { + zwlr_foreign_toplevel_handle_v1_send_state(resource, &states); + } + + wl_array_release(&states); + toplevel_update_idle_source(toplevel); +} + +void wlr_foreign_toplevel_handle_v1_set_maximized( + struct wlr_foreign_toplevel_handle_v1 *toplevel, bool maximized) { + if (maximized == !!(toplevel->state & + WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED)) { + return; + } + if (maximized) { + toplevel->state |= WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED; + } else { + toplevel->state &= ~WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED; + } + toplevel_send_state(toplevel); +} + +void wlr_foreign_toplevel_handle_v1_set_minimized( + struct wlr_foreign_toplevel_handle_v1 *toplevel, bool minimized) { + if (minimized == !!(toplevel->state & + WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED)) { + return; + } + if (minimized) { + toplevel->state |= WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED; + } else { + toplevel->state &= ~WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED; + } + toplevel_send_state(toplevel); +} + +void wlr_foreign_toplevel_handle_v1_set_activated( + struct wlr_foreign_toplevel_handle_v1 *toplevel, bool activated) { + if (activated == !!(toplevel->state & + WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)) { + return; + } + if (activated) { + toplevel->state |= WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED; + } else { + toplevel->state &= ~WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED; + } + toplevel_send_state(toplevel); +} + +void wlr_foreign_toplevel_handle_v1_set_fullscreen( + struct wlr_foreign_toplevel_handle_v1 * toplevel, bool fullscreen) { + if (fullscreen == !!(toplevel->state & + WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN)) { + return; + } + if (fullscreen) { + toplevel->state |= WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN; + } else { + toplevel->state &= ~WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN; + } + toplevel_send_state(toplevel); +} + +static void toplevel_resource_send_parent( + struct wl_resource *toplevel_resource, + struct wlr_foreign_toplevel_handle_v1 *parent) { + if (wl_resource_get_version(toplevel_resource) < + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_PARENT_SINCE_VERSION) { + return; + } + struct wl_client *client = wl_resource_get_client(toplevel_resource); + struct wl_resource *parent_resource = NULL; + if (parent) { + parent_resource = wl_resource_find_for_client(&parent->resources, client); + if (!parent_resource) { + /* don't send an event if this client destroyed the parent handle */ + return; + } + } + zwlr_foreign_toplevel_handle_v1_send_parent(toplevel_resource, + parent_resource); +} + +void wlr_foreign_toplevel_handle_v1_set_parent( + struct wlr_foreign_toplevel_handle_v1 *toplevel, + struct wlr_foreign_toplevel_handle_v1 *parent) { + if (parent == toplevel->parent) { + /* only send parent event to the clients if there was a change */ + return; + } + struct wl_resource *toplevel_resource, *tmp; + wl_resource_for_each_safe(toplevel_resource, tmp, &toplevel->resources) { + toplevel_resource_send_parent(toplevel_resource, parent); + } + toplevel->parent = parent; + toplevel_update_idle_source(toplevel); +} + +void wlr_foreign_toplevel_handle_v1_destroy( + struct wlr_foreign_toplevel_handle_v1 *toplevel) { + if (!toplevel) { + return; + } + + wl_signal_emit_mutable(&toplevel->events.destroy, toplevel); + + struct wl_resource *resource, *tmp; + wl_resource_for_each_safe(resource, tmp, &toplevel->resources) { + zwlr_foreign_toplevel_handle_v1_send_closed(resource); + wl_resource_set_user_data(resource, NULL); + wl_list_remove(wl_resource_get_link(resource)); + wl_list_init(wl_resource_get_link(resource)); + } + + struct wlr_foreign_toplevel_handle_v1_output *toplevel_output, *tmp2; + wl_list_for_each_safe(toplevel_output, tmp2, &toplevel->outputs, link) { + toplevel_output_destroy(toplevel_output); + } + + if (toplevel->idle_source) { + wl_event_source_remove(toplevel->idle_source); + } + + wl_list_remove(&toplevel->link); + + /* need to ensure no other toplevels hold a pointer to this one as + * a parent, so that a later call to foreign_toplevel_manager_bind() + * will not result in a segfault */ + struct wlr_foreign_toplevel_handle_v1 *tl, *tmp3; + wl_list_for_each_safe(tl, tmp3, &toplevel->manager->toplevels, link) { + if (tl->parent == toplevel) { + /* Note: we send a parent signal to all clients in this case; + * the caller should first destroy the child handles if it + * wishes to avoid this behavior. */ + wlr_foreign_toplevel_handle_v1_set_parent(tl, NULL); + } + } + + free(toplevel->title); + free(toplevel->app_id); + free(toplevel); +} + +static void foreign_toplevel_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static struct wl_resource *create_toplevel_resource_for_resource( + struct wlr_foreign_toplevel_handle_v1 *toplevel, + struct wl_resource *manager_resource) { + struct wl_client *client = wl_resource_get_client(manager_resource); + struct wl_resource *resource = wl_resource_create(client, + &zwlr_foreign_toplevel_handle_v1_interface, + wl_resource_get_version(manager_resource), 0); + if (!resource) { + wl_client_post_no_memory(client); + return NULL; + } + + wl_resource_set_implementation(resource, &toplevel_handle_impl, toplevel, + foreign_toplevel_resource_destroy); + + wl_list_insert(&toplevel->resources, wl_resource_get_link(resource)); + zwlr_foreign_toplevel_manager_v1_send_toplevel(manager_resource, resource); + return resource; +} + +struct wlr_foreign_toplevel_handle_v1 * +wlr_foreign_toplevel_handle_v1_create( + struct wlr_foreign_toplevel_manager_v1 *manager) { + struct wlr_foreign_toplevel_handle_v1 *toplevel = calloc(1, + sizeof(struct wlr_foreign_toplevel_handle_v1)); + if (!toplevel) { + return NULL; + } + + wl_list_insert(&manager->toplevels, &toplevel->link); + toplevel->manager = manager; + + wl_list_init(&toplevel->resources); + wl_list_init(&toplevel->outputs); + + wl_signal_init(&toplevel->events.request_maximize); + wl_signal_init(&toplevel->events.request_minimize); + wl_signal_init(&toplevel->events.request_activate); + wl_signal_init(&toplevel->events.request_fullscreen); + wl_signal_init(&toplevel->events.request_close); + wl_signal_init(&toplevel->events.set_rectangle); + wl_signal_init(&toplevel->events.destroy); + + struct wl_resource *manager_resource, *tmp; + wl_resource_for_each_safe(manager_resource, tmp, &manager->resources) { + create_toplevel_resource_for_resource(toplevel, manager_resource); + } + + return toplevel; +} + +static const struct zwlr_foreign_toplevel_manager_v1_interface + foreign_toplevel_manager_impl; + +static void foreign_toplevel_manager_handle_stop(struct wl_client *client, + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwlr_foreign_toplevel_manager_v1_interface, + &foreign_toplevel_manager_impl)); + + zwlr_foreign_toplevel_manager_v1_send_finished(resource); + wl_resource_destroy(resource); +} + +static const struct zwlr_foreign_toplevel_manager_v1_interface + foreign_toplevel_manager_impl = { + .stop = foreign_toplevel_manager_handle_stop +}; + +static void foreign_toplevel_manager_resource_destroy( + struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void toplevel_send_details_to_toplevel_resource( + struct wlr_foreign_toplevel_handle_v1 *toplevel, + struct wl_resource *resource) { + if (toplevel->title) { + zwlr_foreign_toplevel_handle_v1_send_title(resource, toplevel->title); + } + if (toplevel->app_id) { + zwlr_foreign_toplevel_handle_v1_send_app_id(resource, toplevel->app_id); + } + + struct wlr_foreign_toplevel_handle_v1_output *output; + wl_list_for_each(output, &toplevel->outputs, link) { + send_output_to_resource(resource, output->output, true); + } + + struct wl_array states; + wl_array_init(&states); + bool r = fill_array_from_toplevel_state(&states, toplevel->state); + if (!r) { + wl_resource_post_no_memory(resource); + wl_array_release(&states); + return; + } + + zwlr_foreign_toplevel_handle_v1_send_state(resource, &states); + wl_array_release(&states); + + toplevel_resource_send_parent(resource, toplevel->parent); + + zwlr_foreign_toplevel_handle_v1_send_done(resource); +} + +static void foreign_toplevel_manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_foreign_toplevel_manager_v1 *manager = data; + struct wl_resource *resource = wl_resource_create(client, + &zwlr_foreign_toplevel_manager_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &foreign_toplevel_manager_impl, + manager, foreign_toplevel_manager_resource_destroy); + + wl_list_insert(&manager->resources, wl_resource_get_link(resource)); + + struct wlr_foreign_toplevel_handle_v1 *toplevel, *tmp; + /* First loop: create a handle for all toplevels for all clients. + * Separation into two loops avoid the case where a child handle + * is created before a parent handle, so the parent relationship + * could not be sent to a client. */ + wl_list_for_each_safe(toplevel, tmp, &manager->toplevels, link) { + create_toplevel_resource_for_resource(toplevel, resource); + } + /* Second loop: send details about each toplevel. */ + wl_list_for_each_safe(toplevel, tmp, &manager->toplevels, link) { + struct wl_resource *toplevel_resource = + wl_resource_find_for_client(&toplevel->resources, client); + toplevel_send_details_to_toplevel_resource(toplevel, + toplevel_resource); + } +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_foreign_toplevel_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wl_signal_emit_mutable(&manager->events.destroy, manager); + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +struct wlr_foreign_toplevel_manager_v1 *wlr_foreign_toplevel_manager_v1_create( + struct wl_display *display) { + struct wlr_foreign_toplevel_manager_v1 *manager = calloc(1, + sizeof(struct wlr_foreign_toplevel_manager_v1)); + if (!manager) { + return NULL; + } + + manager->event_loop = wl_display_get_event_loop(display); + manager->global = wl_global_create(display, + &zwlr_foreign_toplevel_manager_v1_interface, + FOREIGN_TOPLEVEL_MANAGEMENT_V1_VERSION, manager, + foreign_toplevel_manager_bind); + if (!manager->global) { + free(manager); + return NULL; + } + + wl_signal_init(&manager->events.destroy); + wl_list_init(&manager->resources); + wl_list_init(&manager->toplevels); + + manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} diff --git a/wlr_foreign_toplevel_management_v1.h b/wlr_foreign_toplevel_management_v1.h new file mode 100755 index 0000000..c4abcb3 --- /dev/null +++ b/wlr_foreign_toplevel_management_v1.h @@ -0,0 +1,153 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_FOREIGN_TOPLEVEL_MANAGEMENT_V1_H +#define WLR_TYPES_WLR_FOREIGN_TOPLEVEL_MANAGEMENT_V1_H + +#include +#include + +struct wlr_foreign_toplevel_manager_v1 { + struct wl_event_loop *event_loop; + struct wl_global *global; + struct wl_list resources; // wl_resource_get_link() + struct wl_list toplevels; // wlr_foreign_toplevel_handle_v1.link + + struct wl_listener display_destroy; + + struct { + struct wl_signal destroy; + } events; + + void *data; +}; + +enum wlr_foreign_toplevel_handle_v1_state { + WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED = (1 << 0), + WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED = (1 << 1), + WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED = (1 << 2), + WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN = (1 << 3), +}; + +struct wlr_foreign_toplevel_handle_v1_output { + struct wl_list link; // wlr_foreign_toplevel_handle_v1.outputs + struct wlr_output *output; + struct wlr_foreign_toplevel_handle_v1 *toplevel; + + // private state + + struct wl_listener output_bind; + struct wl_listener output_destroy; +}; + +struct wlr_foreign_toplevel_handle_v1 { + struct wlr_foreign_toplevel_manager_v1 *manager; + struct wl_list resources; + struct wl_list link; + struct wl_event_source *idle_source; + + char *title; + char *app_id; + struct wlr_foreign_toplevel_handle_v1 *parent; + struct wl_list outputs; // wlr_foreign_toplevel_v1_output.link + uint32_t state; // enum wlr_foreign_toplevel_v1_state + + struct { + // struct wlr_foreign_toplevel_handle_v1_maximized_event + struct wl_signal request_maximize; + // struct wlr_foreign_toplevel_handle_v1_minimized_event + struct wl_signal request_minimize; + // struct wlr_foreign_toplevel_handle_v1_activated_event + struct wl_signal request_activate; + // struct wlr_foreign_toplevel_handle_v1_fullscreen_event + struct wl_signal request_fullscreen; + struct wl_signal request_close; + + // struct wlr_foreign_toplevel_handle_v1_set_rectangle_event + struct wl_signal set_rectangle; + struct wl_signal destroy; + } events; + + void *data; +}; + +struct wlr_foreign_toplevel_handle_v1_maximized_event { + struct wlr_foreign_toplevel_handle_v1 *toplevel; + bool maximized; +}; + +struct wlr_foreign_toplevel_handle_v1_minimized_event { + struct wlr_foreign_toplevel_handle_v1 *toplevel; + bool minimized; +}; + +struct wlr_foreign_toplevel_handle_v1_activated_event { + struct wlr_foreign_toplevel_handle_v1 *toplevel; + struct wlr_seat *seat; +}; + +struct wlr_foreign_toplevel_handle_v1_fullscreen_event { + struct wlr_foreign_toplevel_handle_v1 *toplevel; + bool fullscreen; + struct wlr_output *output; +}; + +struct wlr_foreign_toplevel_handle_v1_set_rectangle_event { + struct wlr_foreign_toplevel_handle_v1 *toplevel; + struct wlr_surface *surface; + int32_t x, y, width, height; +}; + +struct wlr_foreign_toplevel_manager_v1 *wlr_foreign_toplevel_manager_v1_create( + struct wl_display *display); + +struct wlr_foreign_toplevel_handle_v1 *wlr_foreign_toplevel_handle_v1_create( + struct wlr_foreign_toplevel_manager_v1 *manager); +/** + * Destroy the given toplevel handle, sending the closed event to any + * client. Also, if the destroyed toplevel is set as a parent of any + * other valid toplevel, clients still holding a handle to both are + * sent a parent signal with NULL parent. If this is not desired, the + * caller should ensure that any child toplevels are destroyed before + * the parent. + */ +void wlr_foreign_toplevel_handle_v1_destroy( + struct wlr_foreign_toplevel_handle_v1 *toplevel); + +void wlr_foreign_toplevel_handle_v1_set_title( + struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *title); +void wlr_foreign_toplevel_handle_v1_set_app_id( + struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *app_id); + +void wlr_foreign_toplevel_handle_v1_output_enter( + struct wlr_foreign_toplevel_handle_v1 *toplevel, struct wlr_output *output); +void wlr_foreign_toplevel_handle_v1_output_leave( + struct wlr_foreign_toplevel_handle_v1 *toplevel, struct wlr_output *output); + +void wlr_foreign_toplevel_handle_v1_set_maximized( + struct wlr_foreign_toplevel_handle_v1 *toplevel, bool maximized); +void wlr_foreign_toplevel_handle_v1_set_minimized( + struct wlr_foreign_toplevel_handle_v1 *toplevel, bool minimized); +void wlr_foreign_toplevel_handle_v1_set_activated( + struct wlr_foreign_toplevel_handle_v1 *toplevel, bool activated); +void wlr_foreign_toplevel_handle_v1_set_fullscreen( + struct wlr_foreign_toplevel_handle_v1* toplevel, bool fullscreen); + +/** + * Set the parent of a toplevel. If the parent changed from its previous + * value, also sends a parent event to all clients that hold handles to + * both toplevel and parent (no message is sent to clients that have + * previously destroyed their parent handle). NULL is allowed as the + * parent, meaning no parent exists. + */ +void wlr_foreign_toplevel_handle_v1_set_parent( + struct wlr_foreign_toplevel_handle_v1 *toplevel, + struct wlr_foreign_toplevel_handle_v1 *parent); + + +#endif