1 /*
2  * SPDX-FileCopyrightText: 2017-2017 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 #include "waylandinputwindow.h"
8 #include "waylandim_public.h"
9 #include "waylandui.h"
10 #include "waylandwindow.h"
11 #include "zwp_input_panel_v1.h"
12 #include "zwp_input_popup_surface_v2.h"
13 
14 #ifdef __linux__
15 #include <linux/input-event-codes.h>
16 #elif __FreeBSD__
17 #include <dev/evdev/input-event-codes.h>
18 #else
19 #define BTN_LEFT 0x110
20 #endif
21 
22 namespace fcitx::classicui {
23 
WaylandInputWindow(WaylandUI * ui)24 WaylandInputWindow::WaylandInputWindow(WaylandUI *ui)
25     : InputWindow(ui->parent()), ui_(ui), window_(ui->newWindow()) {
26     window_->createWindow();
27     window_->repaint().connect([this]() {
28         if (auto *ic = repaintIC_.get()) {
29             if (ic->hasFocus()) {
30                 update(ic);
31             }
32         }
33     });
34     window_->click().connect([this](int x, int y, uint32_t button,
35                                     uint32_t state) {
36         if (state == WL_POINTER_BUTTON_STATE_PRESSED && button == BTN_LEFT) {
37             click(x, y);
38         }
39     });
40     window_->hover().connect([this](int x, int y) {
41         if (hover(x, y)) {
42             repaint();
43         }
44     });
45     window_->leave().connect([this]() {
46         if (hover(-1, -1)) {
47             repaint();
48         }
49     });
50     window_->axis().connect([this](int, int, uint32_t axis, wl_fixed_t value) {
51         if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) {
52             return;
53         }
54         scroll_ += value;
55         bool triggered = false;
56         while (scroll_ >= 2560) {
57             scroll_ -= 2560;
58             wheel(/*up=*/false);
59             triggered = true;
60         }
61         while (scroll_ <= -2560) {
62             scroll_ += 2560;
63             wheel(/*up=*/true);
64             triggered = true;
65         }
66         if (triggered) {
67             repaint();
68         }
69     });
70     initPanel();
71 }
72 
initPanel()73 void WaylandInputWindow::initPanel() {
74     if (panelSurface_) {
75         return;
76     }
77     if (!window_->surface()) {
78         window_->createWindow();
79         return;
80     }
81     auto panel = ui_->display()->getGlobal<wayland::ZwpInputPanelV1>();
82     if (!panel) {
83         return;
84     }
85     panelSurface_.reset(panel->getInputPanelSurface(window_->surface()));
86     panelSurface_->setOverlayPanel();
87 }
88 
resetPanel()89 void WaylandInputWindow::resetPanel() { panelSurface_.reset(); }
90 
update(fcitx::InputContext * ic)91 void WaylandInputWindow::update(fcitx::InputContext *ic) {
92     InputWindow::update(ic);
93 
94     if (!visible()) {
95         window_->hide();
96         return;
97     }
98 
99     assert(!visible() || ic != nullptr);
100     if (ic->frontend() == std::string_view("wayland_v2")) {
101         if (ic != v2IC_.get()) {
102             v2IC_ = ic->watch();
103             auto *im = ui_->parent()
104                            ->waylandim()
105                            ->call<IWaylandIMModule::getInputMethodV2>(ic);
106             panelSurfaceV2_.reset(im->getInputPopupSurface(window_->surface()));
107         }
108     }
109     if (!panelSurface_ && !panelSurfaceV2_) {
110         return;
111     }
112     auto pair = sizeHint();
113     int width = pair.first, height = pair.second;
114 
115     if (width != window_->width() || height != window_->height()) {
116         window_->resize(width, height);
117     }
118 
119     if (auto *surface = window_->prerender()) {
120         cairo_t *c = cairo_create(surface);
121         paint(c, width, height);
122         cairo_destroy(c);
123         window_->render();
124     } else {
125         repaintIC_ = ic->watch();
126     }
127 }
128 
repaint()129 void WaylandInputWindow::repaint() {
130 
131     if (auto *surface = window_->prerender()) {
132         cairo_t *c = cairo_create(surface);
133         paint(c, window_->width(), window_->height());
134         cairo_destroy(c);
135         window_->render();
136     }
137 }
138 
139 } // namespace fcitx::classicui
140