Track global listeners so cleanup cannot miss one

7dba09d76224005165b8762851d84feaecb84f79
Alexis Sellier committed ago 1 parent 55405e9d
swm.c +58 -94
112 112
#define MAX_WINDOW_STATE_LINE   1024
113 113
#define MAX_STATE_PATH          4096
114 114
#define MAX_WORKSPACE_TITLE     256
115 115
#define MAX_WS_ID               16
116 116
#define MAX_STATIC_LISTENERS    512
117 +
#define MAX_GLOBAL_LISTENERS    64
117 118
#define MAX_WS_MANAGERS         64
118 119
#define MAX_WS_HANDLES          (MAX_WS_MANAGERS * WSCOUNT)
119 120
#define MAX_AUTOSTART           64
120 121
#define MAX_COMMAND_SIZE        1024
121 122
#define MAX_COMMAND_ARGS        64
535 536
static void      key_press_modifiers(struct wl_listener *listener, void *data);
536 537
static void      load_window_states(void);
537 538
static int       key_repeat(void *data);
538 539
static void      kill_client(const arg_t *arg);
539 540
static void      lock_session(struct wl_listener *listener, void *data);
541 +
static void      listen_global(struct wl_signal *signal, struct wl_listener *listener);
540 542
static void      listen_static(struct wl_signal *signal, wl_notify_func_t notify);
541 543
static void      listener_release(struct wl_listener *listener);
542 544
static void      map_notify(struct wl_listener *listener, void *data);
543 545
static void      maximize_notify(struct wl_listener *listener, void *data);
544 546
static void      max_stack(monitor_t *m);
655 657
static bool                    locked;
656 658
static void                   *exclusive_focus;
657 659
static struct wl_display      *dpy;
658 660
static struct wl_event_loop   *event_loop;
659 661
static struct wl_event_source *signal_sources[3];
662 +
static struct wl_listener     *global_listeners[MAX_GLOBAL_LISTENERS];
663 +
static size_t                  global_listener_count;
660 664
static sigset_t                original_signal_mask;
661 665
static struct wlr_backend     *backend;
662 666
static struct wlr_scene       *scene;
663 667
static struct wlr_scene_tree  *layers[NUM_LAYERS];
664 668
static struct wlr_scene_tree  *drag_icon;
786 790
static void                 set_hints(struct wl_listener *listener, void *data);
787 791
static void                 xwayland_ready(struct wl_listener *listener, void *data);
788 792
static struct wl_listener   new_xwayland_surface    = { .notify = create_notify_x11 };
789 793
static struct wl_listener   xwayland_ready_listener = { .notify = xwayland_ready };
790 794
static struct wlr_xwayland *xwayland;
791 -
static bool                 xwayland_listeners_registered;
792 795
793 796
/* Load configuration after declaring the types and state it may use. */
794 797
#include "config.h"
795 798
796 799
static_assert(WSCOUNT > 0 && WSCOUNT <= 32, "WSCOUNT must be between 1 and 32");
2063 2066
    close_monitor(m);
2064 2067
    wlr_scene_node_destroy(&m->fullscreen_bg->node);
2065 2068
    pool_release(&monitor_pool, m);
2066 2069
}
2067 2070
2071 +
/* Attach a listener that stays connected for the compositor's lifetime. */
2072 +
void listen_global(struct wl_signal *signal, struct wl_listener *listener) {
2073 +
    if (global_listener_count >= LENGTH(global_listeners))
2074 +
        die("too many global listeners");
2075 +
    global_listeners[global_listener_count++] = listener;
2076 +
    wl_signal_add(signal, listener);
2077 +
}
2078 +
2068 2079
/* Detach global event listeners before their event sources are destroyed. */
2069 2080
void cleanup_listeners(void) {
2070 -
    wl_list_remove(&cursor_axis.link);
2071 -
    wl_list_remove(&cursor_button.link);
2072 -
    wl_list_remove(&cursor_frame_listener.link);
2073 -
    wl_list_remove(&cursor_motion.link);
2074 -
    wl_list_remove(&cursor_motion_absolute.link);
2075 -
    wl_list_remove(&cursor_swipe_begin.link);
2076 -
    wl_list_remove(&cursor_swipe_update.link);
2077 -
    wl_list_remove(&cursor_swipe_end.link);
2078 -
    wl_list_remove(&cursor_pinch_begin.link);
2079 -
    wl_list_remove(&cursor_pinch_update.link);
2080 -
    wl_list_remove(&cursor_pinch_end.link);
2081 -
    wl_list_remove(&cursor_hold_begin.link);
2082 -
    wl_list_remove(&cursor_hold_end.link);
2083 -
    wl_list_remove(&gpu_reset_listener.link);
2084 -
    wl_list_remove(&new_idle_inhibitor.link);
2085 -
    wl_list_remove(&layout_change.link);
2086 -
    wl_list_remove(&new_input_device.link);
2087 -
    wl_list_remove(&new_input_method.link);
2088 -
    wl_list_remove(&new_virtual_keyboard.link);
2089 -
    wl_list_remove(&new_virtual_pointer.link);
2090 -
    wl_list_remove(&new_pointer_constraint.link);
2091 -
    wl_list_remove(&new_shortcuts_inhibitor_listener.link);
2092 -
    wl_list_remove(&new_output.link);
2093 -
    wl_list_remove(&new_xdg_toplevel.link);
2094 -
    wl_list_remove(&new_text_input.link);
2095 -
    wl_list_remove(&new_xdg_decoration.link);
2096 -
    wl_list_remove(&new_xdg_dialog.link);
2097 -
    wl_list_remove(&new_xdg_popup.link);
2098 -
    wl_list_remove(&new_layer_surface.link);
2099 -
    wl_list_remove(&ext_ftl_capture_request.link);
2100 -
    wl_list_remove(&output_mgr_apply.link);
2101 -
    wl_list_remove(&output_mgr_test.link);
2102 -
    wl_list_remove(&output_power_mgr_set_mode.link);
2103 -
    wl_list_remove(&request_activate.link);
2104 -
    wl_list_remove(&system_bell_ring.link);
2105 -
    wl_list_remove(&request_cursor.link);
2106 -
    wl_list_remove(&request_set_psel.link);
2107 -
    wl_list_remove(&request_set_sel.link);
2108 -
    wl_list_remove(&request_set_cursor_shape.link);
2109 -
    wl_list_remove(&request_start_drag_listener.link);
2110 -
    wl_list_remove(&start_drag_listener.link);
2111 -
    wl_list_remove(&new_session_lock.link);
2112 -
2113 -
    if (xwayland_listeners_registered) {
2114 -
        wl_list_remove(&new_xwayland_surface.link);
2115 -
        wl_list_remove(&xwayland_ready_listener.link);
2116 -
        xwayland_listeners_registered = false;
2117 -
    }
2081 +
    while (global_listener_count)
2082 +
        wl_list_remove(&global_listeners[--global_listener_count]->link);
2118 2083
}
2119 2084
2120 2085
/* Hide a display's workspace and move its windows to a valid display. */
2121 2086
void close_monitor(monitor_t *m) {
2122 2087
    /* If the selected display closes, select another one. Its workspace
5663 5628
    wlr_scene_node_place_below(&drag_icon->node, &layers[LAYER_BLOCK]->node);
5664 5629
5665 5630
    /* Choose a renderer. WLR_RENDERER can override the automatic choice. */
5666 5631
    if (!(drw = wlr_renderer_autocreate(backend)))
5667 5632
        die("couldn't create renderer");
5668 -
    wl_signal_add(&drw->events.lost, &gpu_reset_listener);
5633 +
    listen_global(&drw->events.lost, &gpu_reset_listener);
5669 5634
5670 5635
    /* Advertise the buffer types supported by both the renderer and scene code. */
5671 5636
    wlr_renderer_init_wl_shm(drw, dpy);
5672 5637
5673 5638
    if (wlr_renderer_get_texture_formats(drw, WLR_BUFFER_CAP_DMABUF)) {
5697 5662
    wlr_presentation_create(dpy, backend, 2);
5698 5663
    wlr_alpha_modifier_v1_create(dpy);
5699 5664
5700 5665
    /* Accept application requests for attention. */
5701 5666
    activation = wlr_xdg_activation_v1_create(dpy);
5702 -
    wl_signal_add(&activation->events.request_activate, &request_activate);
5667 +
    listen_global(&activation->events.request_activate, &request_activate);
5703 5668
5704 5669
    system_bell = wlr_xdg_system_bell_v1_create(dpy, 1);
5705 -
    wl_signal_add(&system_bell->events.ring, &system_bell_ring);
5670 +
    listen_global(&system_bell->events.ring, &system_bell_ring);
5706 5671
5707 5672
    wlr_scene_set_gamma_control_manager_v1(scene, wlr_gamma_control_manager_v1_create(dpy));
5708 5673
5709 5674
    power_mgr = wlr_output_power_manager_v1_create(dpy);
5710 -
    wl_signal_add(&power_mgr->events.set_mode, &output_power_mgr_set_mode);
5675 +
    listen_global(&power_mgr->events.set_mode, &output_power_mgr_set_mode);
5711 5676
5712 5677
    /* Track the position of each display in the combined desktop. */
5713 5678
    output_layout = wlr_output_layout_create(dpy);
5714 -
    wl_signal_add(&output_layout->events.change, &layout_change);
5679 +
    listen_global(&output_layout->events.change, &layout_change);
5715 5680
5716 5681
    wlr_xdg_output_manager_v1_create(dpy, output_layout);
5717 5682
5718 5683
    /* Workspaces are global and independent of outputs. */
5719 5684
    for (i = 0; i < WSCOUNT; i++) {
5736 5701
    /* Publish window lists for taskbars and switchers. */
5737 5702
    ftl_mgr      = wlr_foreign_toplevel_manager_v1_create(dpy);
5738 5703
    ext_ftl_list = wlr_ext_foreign_toplevel_list_v1_create(dpy, 1);
5739 5704
    wlr_ext_image_copy_capture_manager_v1_create(dpy, 1);
5740 5705
    ext_ftl_capture_mgr = wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(dpy, 1);
5741 -
    wl_signal_add(&ext_ftl_capture_mgr->events.new_request, &ext_ftl_capture_request);
5706 +
    listen_global(&ext_ftl_capture_mgr->events.new_request, &ext_ftl_capture_request);
5742 5707
5743 5708
    /* Let virtual machines and remote desktops capture window-manager shortcuts. */
5744 5709
    kb_inhibit_mgr = wlr_keyboard_shortcuts_inhibit_v1_create(dpy);
5745 -
    wl_signal_add(&kb_inhibit_mgr->events.new_inhibitor, &new_shortcuts_inhibitor_listener);
5710 +
    listen_global(&kb_inhibit_mgr->events.new_inhibitor, &new_shortcuts_inhibitor_listener);
5746 5711
5747 5712
    /* Listen for newly connected displays. */
5748 5713
    wl_list_init(&mons);
5749 -
    wl_signal_add(&backend->events.new_output, &new_output);
5714 +
    listen_global(&backend->events.new_output, &new_output);
5750 5715
5751 5716
    /* Set up application windows, panels, backgrounds, and other desktop surfaces. */
5752 5717
    wl_list_init(&clients);
5753 5718
    wl_list_init(&fstack);
5754 5719
5755 5720
    xdg_shell = wlr_xdg_shell_create(dpy, 6);
5756 -
    wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
5757 -
    wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup);
5721 +
    listen_global(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
5722 +
    listen_global(&xdg_shell->events.new_popup, &new_xdg_popup);
5758 5723
5759 5724
    xdg_dialog_mgr = wlr_xdg_wm_dialog_v1_create(dpy, 1);
5760 -
    wl_signal_add(&xdg_dialog_mgr->events.new_dialog, &new_xdg_dialog);
5725 +
    listen_global(&xdg_dialog_mgr->events.new_dialog, &new_xdg_dialog);
5761 5726
5762 5727
    layer_shell = wlr_layer_shell_v1_create(dpy, 4);
5763 -
    wl_signal_add(&layer_shell->events.new_surface, &new_layer_surface);
5728 +
    listen_global(&layer_shell->events.new_surface, &new_layer_surface);
5764 5729
5765 5730
    idle_notifier = wlr_idle_notifier_v1_create(dpy);
5766 5731
5767 5732
    idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy);
5768 -
    wl_signal_add(&idle_inhibit_mgr->events.new_inhibitor, &new_idle_inhibitor);
5733 +
    listen_global(&idle_inhibit_mgr->events.new_inhibitor, &new_idle_inhibitor);
5769 5734
5770 5735
    session_lock_mgr = wlr_session_lock_manager_v1_create(dpy);
5771 -
    wl_signal_add(&session_lock_mgr->events.new_lock, &new_session_lock);
5736 +
    listen_global(&session_lock_mgr->events.new_lock, &new_session_lock);
5772 5737
    locked_bg = wlr_scene_rect_create(
5773 5738
        layers[LAYER_BLOCK], sgeom.width, sgeom.height, (float[4]){ 0.1f, 0.1f, 0.1f, 1.0f }
5774 5739
    );
5775 5740
5776 5741
    if (!locked_bg)
5780 5745
    /* Prefer window borders drawn by the compositor. */
5781 5746
    wlr_server_decoration_manager_set_default_mode(
5782 5747
        wlr_server_decoration_manager_create(dpy), WLR_SERVER_DECORATION_MANAGER_MODE_SERVER
5783 5748
    );
5784 5749
    xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy);
5785 -
    wl_signal_add(&xdg_decoration_mgr->events.new_toplevel_decoration, &new_xdg_decoration);
5750 +
    listen_global(&xdg_decoration_mgr->events.new_toplevel_decoration, &new_xdg_decoration);
5786 5751
5787 5752
    pointer_constraints = wlr_pointer_constraints_v1_create(dpy);
5788 -
    wl_signal_add(&pointer_constraints->events.new_constraint, &new_pointer_constraint);
5753 +
    listen_global(&pointer_constraints->events.new_constraint, &new_pointer_constraint);
5789 5754
5790 5755
    relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy);
5791 5756
    pointer_gestures     = wlr_pointer_gestures_v1_create(dpy);
5792 5757
5793 5758
    /* Track the pointer across all displays. */
5797 5762
    /* Load cursor images at the scales required by connected displays. */
5798 5763
    cursor_mgr = wlr_xcursor_manager_create(nullptr, 24);
5799 5764
    setenv("XCURSOR_SIZE", "24", 1);
5800 5765
5801 5766
    /* Combine pointer devices and handle their movement, buttons, scrolling, and frames. */
5802 -
    wl_signal_add(&cursor->events.motion, &cursor_motion);
5803 -
    wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute);
5804 -
    wl_signal_add(&cursor->events.button, &cursor_button);
5805 -
    wl_signal_add(&cursor->events.axis, &cursor_axis);
5806 -
    wl_signal_add(&cursor->events.frame, &cursor_frame_listener);
5807 -
    wl_signal_add(&cursor->events.swipe_begin, &cursor_swipe_begin);
5808 -
    wl_signal_add(&cursor->events.swipe_update, &cursor_swipe_update);
5809 -
    wl_signal_add(&cursor->events.swipe_end, &cursor_swipe_end);
5810 -
    wl_signal_add(&cursor->events.pinch_begin, &cursor_pinch_begin);
5811 -
    wl_signal_add(&cursor->events.pinch_update, &cursor_pinch_update);
5812 -
    wl_signal_add(&cursor->events.pinch_end, &cursor_pinch_end);
5813 -
    wl_signal_add(&cursor->events.hold_begin, &cursor_hold_begin);
5814 -
    wl_signal_add(&cursor->events.hold_end, &cursor_hold_end);
5767 +
    listen_global(&cursor->events.motion, &cursor_motion);
5768 +
    listen_global(&cursor->events.motion_absolute, &cursor_motion_absolute);
5769 +
    listen_global(&cursor->events.button, &cursor_button);
5770 +
    listen_global(&cursor->events.axis, &cursor_axis);
5771 +
    listen_global(&cursor->events.frame, &cursor_frame_listener);
5772 +
    listen_global(&cursor->events.swipe_begin, &cursor_swipe_begin);
5773 +
    listen_global(&cursor->events.swipe_update, &cursor_swipe_update);
5774 +
    listen_global(&cursor->events.swipe_end, &cursor_swipe_end);
5775 +
    listen_global(&cursor->events.pinch_begin, &cursor_pinch_begin);
5776 +
    listen_global(&cursor->events.pinch_update, &cursor_pinch_update);
5777 +
    listen_global(&cursor->events.pinch_end, &cursor_pinch_end);
5778 +
    listen_global(&cursor->events.hold_begin, &cursor_hold_begin);
5779 +
    listen_global(&cursor->events.hold_end, &cursor_hold_end);
5815 5780
5816 5781
    cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
5817 -
    wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape);
5782 +
    listen_global(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape);
5818 5783
5819 5784
    /* A seat groups the input devices controlled by one user. */
5820 -
    wl_signal_add(&backend->events.new_input, &new_input_device);
5785 +
    listen_global(&backend->events.new_input, &new_input_device);
5821 5786
    virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy);
5822 -
    wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard, &new_virtual_keyboard);
5787 +
    listen_global(&virtual_keyboard_mgr->events.new_virtual_keyboard, &new_virtual_keyboard);
5823 5788
    virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(dpy);
5824 -
    wl_signal_add(&virtual_pointer_mgr->events.new_virtual_pointer, &new_virtual_pointer);
5789 +
    listen_global(&virtual_pointer_mgr->events.new_virtual_pointer, &new_virtual_pointer);
5825 5790
5826 5791
    seat = wlr_seat_create(dpy, "seat0");
5827 -
    wl_signal_add(&seat->events.request_set_cursor, &request_cursor);
5828 -
    wl_signal_add(&seat->events.request_set_selection, &request_set_sel);
5829 -
    wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel);
5830 -
    wl_signal_add(&seat->events.request_start_drag, &request_start_drag_listener);
5831 -
    wl_signal_add(&seat->events.start_drag, &start_drag_listener);
5792 +
    listen_global(&seat->events.request_set_cursor, &request_cursor);
5793 +
    listen_global(&seat->events.request_set_selection, &request_set_sel);
5794 +
    listen_global(&seat->events.request_set_primary_selection, &request_set_psel);
5795 +
    listen_global(&seat->events.request_start_drag, &request_start_drag_listener);
5796 +
    listen_global(&seat->events.start_drag, &start_drag_listener);
5832 5797
5833 5798
    /* Connect applications to an input method such as fcitx5. */
5834 5799
    im_mgr = wlr_input_method_manager_v2_create(dpy);
5835 -
    wl_signal_add(&im_mgr->events.new_input_method, &new_input_method);
5800 +
    listen_global(&im_mgr->events.new_input_method, &new_input_method);
5836 5801
    ti_mgr = wlr_text_input_manager_v3_create(dpy);
5837 -
    wl_signal_add(&ti_mgr->events.new_text_input, &new_text_input);
5802 +
    listen_global(&ti_mgr->events.new_text_input, &new_text_input);
5838 5803
5839 5804
    kb_group = create_keyboard_group(false);
5840 5805
5841 5806
    if (!kb_group)
5842 5807
        die("failed to allocate primary keyboard group");
5843 5808
    wl_list_init(&kb_group->destroy.link);
5844 5809
5845 5810
    output_mgr = wlr_output_manager_v1_create(dpy);
5846 -
    wl_signal_add(&output_mgr->events.apply, &output_mgr_apply);
5847 -
    wl_signal_add(&output_mgr->events.test, &output_mgr_test);
5811 +
    listen_global(&output_mgr->events.apply, &output_mgr_apply);
5812 +
    listen_global(&output_mgr->events.test, &output_mgr_test);
5848 5813
5849 5814
    /* Keep XWayland applications on this compositor rather than a parent X server. */
5850 5815
    unsetenv("DISPLAY");
5851 5816
    /* Create the XWayland server; it starts when the first X11 application opens. */
5852 5817
    if ((xwayland = wlr_xwayland_create(dpy, compositor, 1))) {
5853 -
        wl_signal_add(&xwayland->events.ready, &xwayland_ready_listener);
5854 -
        wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface);
5855 -
        xwayland_listeners_registered = true;
5818 +
        listen_global(&xwayland->events.ready, &xwayland_ready_listener);
5819 +
        listen_global(&xwayland->events.new_surface, &new_xwayland_surface);
5856 5820
5857 5821
        setenv("DISPLAY", xwayland->display_name, 1);
5858 5822
    } else {
5859 5823
        fprintf(stderr, "failed to setup XWayland X server, continuing without it\n");
5860 5824
    }