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