Harden output lifecycle state handling

fa8cd67132e8c89565d8b0526f9022820a53d8dc
Alexis Sellier committed ago 1 parent 273d34f6
swm.c +42 -15
315 315
    int                                 layout_x; /* Last configured layout position. */
316 316
    int                                 layout_y;
317 317
    struct wl_list                      layers[4]; /* Layer surfaces on this display. */
318 318
    workspace_t                        *ws;        /* Workspace shown on this display. */
319 319
    workspace_t *previous_workspace;               /* Workspace shown before the current one. */
320 +
    workspace_t *disabled_workspace; /* Workspace hidden while the output is disabled. */
320 321
    int          gamma_lut_changed;
321 322
    int          asleep;
322 323
};
323 324
324 325
/* Each workspace keeps separate settings for vertical and horizontal layouts. */
2157 2158
                selmon = candidate;
2158 2159
                break;
2159 2160
            }
2160 2161
        }
2161 2162
    }
2162 -
    if (m->ws)
2163 -
        m->ws->mon = nullptr;
2163 +
    if (m->ws) {
2164 +
        m->disabled_workspace = m->ws;
2165 +
        m->ws->mon            = nullptr;
2166 +
    }
2164 2167
    m->ws = m->previous_workspace = nullptr;
2165 2168
2166 2169
    for (ws = workspaces; ws < END(workspaces); ws++) {
2167 2170
        if (ws->mon == m)
2168 2171
            ws->mon = nullptr;
2586 2589
     * monitor supports only a specific set of modes. We just pick the
2587 2590
     * monitor's preferred mode; a more sophisticated compositor would let
2588 2591
     * the user configure it. */
2589 2592
    wlr_output_state_set_mode(&state, wlr_output_preferred_mode(wlr_output));
2590 2593
2591 -
    /* Listen for display events. */
2594 +
    wlr_output_state_set_enabled(&state, 1);
2595 +
2596 +
    if (!wlr_output_commit_state(wlr_output, &state)) {
2597 +
        wlr_output_state_finish(&state);
2598 +
2599 +
        if (m->ws)
2600 +
            m->ws->mon = nullptr;
2601 +
        wlr_output->data = nullptr;
2602 +
        pool_release(&monitor_pool, m);
2603 +
        fprintf(stderr, "swm: failed to enable output %s\n", wlr_output->name);
2604 +
        return;
2605 +
    }
2606 +
    wlr_output_state_finish(&state);
2607 +
2608 +
    /* Listen for display events after its initial state has committed. */
2592 2609
    LISTEN(&wlr_output->events.frame, &m->frame, render_monitor);
2593 2610
    LISTEN(&wlr_output->events.destroy, &m->destroy, cleanup_monitor);
2594 2611
    LISTEN(&wlr_output->events.request_state, &m->request_state, request_monitor_state);
2595 2612
    LISTEN(&wlr_output->events.bind, &m->output_bind, workspace_output_bind);
2596 2613
2597 -
    wlr_output_state_set_enabled(&state, 1);
2598 -
    wlr_output_commit_state(wlr_output, &state);
2599 -
    wlr_output_state_finish(&state);
2600 -
2601 2614
    wl_list_insert(&mons, &m->link);
2602 2615
    print_status();
2603 2616
2604 2617
    /* Hide unrelated content behind a transparent fullscreen window, as the
2605 2618
     * XDG shell protocol requires. */
4808 4821
}
4809 4822
4810 4823
/* Turn a display on or off at an external client's request. */
4811 4824
void power_manager_set_mode(struct wl_listener *listener, void *data) {
4812 4825
    struct wlr_output_power_v1_set_mode_event *event = data;
4813 -
    struct wlr_output_state                    state = {};
4814 -
    monitor_t                                 *m     = event->output->data;
4826 +
    struct wlr_output_state                    state;
4827 +
    monitor_t                                 *m = event->output->data;
4815 4828
4816 4829
    if (!m)
4817 4830
        return;
4818 4831
4819 -
    m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the output */
4832 +
    wlr_output_state_init(&state);
4820 4833
    wlr_output_state_set_enabled(&state, event->mode);
4821 -
    wlr_output_commit_state(m->wlr_output, &state);
4822 4834
4823 -
    m->asleep = !event->mode;
4824 -
    update_monitors(nullptr, nullptr);
4835 +
    if (wlr_output_commit_state(m->wlr_output, &state)) {
4836 +
        m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the output */
4837 +
        m->asleep            = !event->mode;
4838 +
        update_monitors(nullptr, nullptr);
4839 +
    }
4840 +
    wlr_output_state_finish(&state);
4825 4841
}
4826 4842
4827 4843
/* Stop the Wayland event loop and begin shutdown. */
4828 4844
void quit(const arg_t *arg) {
4829 4845
    wl_display_terminate(dpy);
4886 4902
4887 4903
/* Commit a display-state change requested by the backend. */
4888 4904
void request_monitor_state(struct wl_listener *listener, void *data) {
4889 4905
    struct wlr_output_event_request_state *event = data;
4890 4906
4891 -
    wlr_output_commit_state(event->output, event->state);
4892 -
    update_monitors(nullptr, nullptr);
4907 +
    if (wlr_output_commit_state(event->output, event->state))
4908 +
        update_monitors(nullptr, nullptr);
4893 4909
}
4894 4910
4895 4911
/* Request a new window geometry and apply it when the application is ready. */
4896 4912
void resize(client_t *c, struct wlr_box geo, bool interact) {
4897 4913
    struct wlr_box *bbox;
6389 6405
        if (m->lock_surface) {
6390 6406
            struct wlr_scene_tree *scene_tree = m->lock_surface->surface->data;
6391 6407
            wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y);
6392 6408
            wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width, m->m.height);
6393 6409
        }
6410 +
        if (!m->ws) {
6411 +
            workspace_t *ws = m->disabled_workspace;
6412 +
6413 +
            if (!ws || ws->mon)
6414 +
                ws = free_workspace();
6415 +
            m->disabled_workspace = nullptr;
6416 +
            m->ws = m->previous_workspace = ws;
6417 +
6418 +
            if (ws)
6419 +
                ws->mon = m;
6420 +
        }
6394 6421
        /* Reserve panel space before arranging regular windows. */
6395 6422
        arrange_layers(m);
6396 6423
        /* Sync clients of this output's workspace (it may have been
6397 6424
         * hidden or shown elsewhere before this display appeared). */
6398 6425
        if (m->ws)
test/run.py +4 -0
688 688
        protocol("output-management", "configure-second")
689 689
        protocol("output-management", "disable-second")
690 690
        wait_title("multi")
691 691
        protocol("output-management", "enable-second")
692 692
        wait_title("multi")
693 +
        virtual_keyboard(106, 4)
694 +
        wait_title("")
695 +
        virtual_keyboard(106, 1)
696 +
        wait_title("multi")
693 697
694 698
    test("multiple outputs", multioutput)
695 699
    terminate(client)
696 700
697 701