TYPE3
[iec.git] / src / type3_AndroidCloud / anbox-master / src / anbox / platform / sdl / platform.cpp
1 /*
2  * Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
3  *
4  * This program is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 3, as published
6  * by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranties of
10  * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11  * PURPOSE.  See the GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17
18 #pragma GCC diagnostic push
19 #pragma GCC diagnostic ignored "-Wswitch-default"
20 #include "anbox/platform/sdl/platform.h"
21 #include "anbox/input/device.h"
22 #include "anbox/input/manager.h"
23 #include "anbox/logger.h"
24 #include "anbox/platform/sdl/keycode_converter.h"
25 #include "anbox/platform/sdl/window.h"
26 #include "anbox/platform/sdl/audio_sink.h"
27 #include "anbox/wm/manager.h"
28
29 #include <boost/throw_exception.hpp>
30
31 #include <signal.h>
32 #include <sys/types.h>
33 #pragma GCC diagnostic pop
34
35 namespace anbox {
36 namespace platform {
37 namespace sdl {
38 Platform::Platform(
39     const std::shared_ptr<input::Manager> &input_manager,
40     const Configuration &config)
41     : input_manager_(input_manager),
42       event_thread_running_(false),
43       config_(config) {
44
45   // Don't block the screensaver from kicking in. It will be blocked
46   // by the desktop shell already and we don't have to do this again.
47   // If we would leave this enabled it will prevent systems from
48   // suspending correctly.
49   SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1");
50
51 #ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
52   // Don't disable compositing
53   // Available since SDL 2.0.8
54   SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
55 #endif
56
57   if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS) < 0) {
58     const auto message = utils::string_format("Failed to initialize SDL: %s", SDL_GetError());
59     BOOST_THROW_EXCEPTION(std::runtime_error(message));
60   }
61
62   auto display_frame = graphics::Rect::Invalid;
63   if (config_.display_frame == graphics::Rect::Invalid) {
64     for (auto n = 0; n < SDL_GetNumVideoDisplays(); n++) {
65       SDL_Rect r;
66       if (SDL_GetDisplayBounds(n, &r) != 0) continue;
67
68       graphics::Rect frame{r.x, r.y, r.x + r.w, r.y + r.h};
69
70       if (display_frame == graphics::Rect::Invalid)
71         display_frame = frame;
72       else
73         display_frame.merge(frame);
74     }
75
76     if (display_frame == graphics::Rect::Invalid)
77       BOOST_THROW_EXCEPTION(
78           std::runtime_error("No valid display configuration found"));
79   } else {
80     display_frame = config_.display_frame;
81     window_size_immutable_ = true;
82   }
83
84   graphics::emugl::DisplayInfo::get()->set_resolution(display_frame.width(), display_frame.height());
85   display_frame_ = display_frame;
86
87   pointer_ = input_manager->create_device();
88   pointer_->set_name("anbox-pointer");
89   pointer_->set_driver_version(1);
90   pointer_->set_input_id({BUS_VIRTUAL, 2, 2, 2});
91   pointer_->set_physical_location("none");
92   pointer_->set_key_bit(BTN_MOUSE);
93   // NOTE: We don't use REL_X/REL_Y in reality but have to specify them here
94   // to allow InputFlinger to detect we're a cursor device.
95   pointer_->set_rel_bit(REL_X);
96   pointer_->set_rel_bit(REL_Y);
97   pointer_->set_rel_bit(REL_HWHEEL);
98   pointer_->set_rel_bit(REL_WHEEL);
99   pointer_->set_prop_bit(INPUT_PROP_POINTER);
100
101   keyboard_ = input_manager->create_device();
102   keyboard_->set_name("anbox-keyboard");
103   keyboard_->set_driver_version(1);
104   keyboard_->set_input_id({BUS_VIRTUAL, 3, 3, 3});
105   keyboard_->set_physical_location("none");
106   keyboard_->set_key_bit(BTN_MISC);
107   keyboard_->set_key_bit(KEY_OK);
108
109   touch_ = input_manager->create_device();
110   touch_->set_name("anbox-touch");
111   touch_->set_driver_version(1);
112   touch_->set_input_id({BUS_VIRTUAL, 4, 4, 4});
113   touch_->set_physical_location("none");
114   touch_->set_abs_bit(ABS_MT_SLOT);
115   touch_->set_abs_max(ABS_MT_SLOT, 10);
116   touch_->set_abs_bit(ABS_MT_TOUCH_MAJOR);
117   touch_->set_abs_max(ABS_MT_TOUCH_MAJOR, 127);
118   touch_->set_abs_bit(ABS_MT_TOUCH_MINOR);
119   touch_->set_abs_max(ABS_MT_TOUCH_MINOR, 127);
120   touch_->set_abs_bit(ABS_MT_POSITION_X);
121   touch_->set_abs_max(ABS_MT_POSITION_X, display_frame.width());
122   touch_->set_abs_bit(ABS_MT_POSITION_Y);
123   touch_->set_abs_max(ABS_MT_POSITION_Y, display_frame.height());
124   touch_->set_abs_bit(ABS_MT_TRACKING_ID);
125   touch_->set_abs_max(ABS_MT_TRACKING_ID, MAX_TRACKING_ID);
126   touch_->set_prop_bit(INPUT_PROP_DIRECT);
127
128   for (int i = 0; i < MAX_FINGERS; i++)
129       touch_slots[i] = -1;
130
131   event_thread_ = std::thread(&Platform::process_events, this);
132 }
133
134 Platform::~Platform() {
135   if (event_thread_running_) {
136     event_thread_running_ = false;
137     event_thread_.join();
138   }
139 }
140
141 void Platform::set_renderer(const std::shared_ptr<Renderer> &renderer) {
142   renderer_ = renderer;
143 }
144
145 void Platform::set_window_manager(const std::shared_ptr<wm::Manager> &window_manager) {
146   window_manager_ = window_manager;
147 }
148
149 void Platform::process_events() {
150   event_thread_running_ = true;
151
152   while (event_thread_running_) {
153     SDL_Event event;
154     while (SDL_WaitEventTimeout(&event, 100)) {
155       switch (event.type) {
156         case SDL_QUIT:
157           break;
158         case SDL_WINDOWEVENT:
159           for (auto &iter : windows_) {
160             if (auto w = iter.second.lock()) {
161               if (w->window_id() == event.window.windowID) {
162                 w->process_event(event);
163                 break;
164               }
165             }
166           }
167           break;
168         case SDL_KEYDOWN:
169         case SDL_KEYUP:
170           if (keyboard_)
171             process_input_event(event);
172           break;
173         case SDL_MOUSEMOTION:
174         case SDL_MOUSEBUTTONDOWN:
175         case SDL_MOUSEBUTTONUP:
176         case SDL_MOUSEWHEEL:
177         case SDL_FINGERDOWN:
178         case SDL_FINGERUP:
179         case SDL_FINGERMOTION:
180           process_input_event(event);
181           break;
182         default:
183           break;
184       }
185     }
186   }
187 }
188
189 void Platform::process_input_event(const SDL_Event &event) {
190   std::vector<input::Event> mouse_events;
191   std::vector<input::Event> keyboard_events;
192   std::vector<input::Event> touch_events;
193
194   std::int32_t x = 0;
195   std::int32_t y = 0;
196
197   switch (event.type) {
198     // Mouse
199     case SDL_MOUSEBUTTONDOWN:
200       if (config_.no_touch_emulation) {
201         mouse_events.push_back({EV_KEY, BTN_LEFT, 1});
202       } else {
203         x = event.button.x;
204         y = event.button.y;
205         if (!adjust_coordinates(x, y))
206           break;
207         push_finger_down(x, y, emulated_touch_id_, touch_events);
208       }
209       break;
210     case SDL_MOUSEBUTTONUP:
211       if (config_.no_touch_emulation) {
212         mouse_events.push_back({EV_KEY, BTN_LEFT, 0});
213       } else {
214         push_finger_up(emulated_touch_id_, touch_events);
215       }
216       break;
217     case SDL_MOUSEMOTION:
218       x = event.motion.x;
219       y = event.motion.y;
220       if (!adjust_coordinates(x, y))
221         break;
222
223       if (config_.no_touch_emulation) {
224         // NOTE: Sending relative move events doesn't really work and we have
225         // changes in libinputflinger to take ABS_X/ABS_Y instead for absolute
226         // position events.
227         mouse_events.push_back({EV_ABS, ABS_X, x});
228         mouse_events.push_back({EV_ABS, ABS_Y, y});
229         // We're sending relative position updates here too but they will be only
230         // used by the Android side EventHub/InputReader to determine if the cursor
231         // was moved. They are not used to find out the exact position.
232         mouse_events.push_back({EV_REL, REL_X, event.motion.xrel});
233         mouse_events.push_back({EV_REL, REL_Y, event.motion.yrel});
234       } else {
235         push_finger_motion(x, y, emulated_touch_id_, touch_events);
236       }
237       break;
238     case SDL_MOUSEWHEEL:
239       if (!config_.no_touch_emulation) {
240         SDL_GetMouseState(&x, &y);
241         if (!adjust_coordinates(x, y))
242           break;
243
244         mouse_events.push_back({EV_ABS, ABS_X, x});
245         mouse_events.push_back({EV_ABS, ABS_Y, y});
246       }
247       mouse_events.push_back(
248           {EV_REL, REL_WHEEL, static_cast<std::int32_t>(event.wheel.y)});
249       break;
250     // Keyboard
251     case SDL_KEYDOWN: {
252       const auto code = KeycodeConverter::convert(event.key.keysym.scancode);
253       if (code == KEY_RESERVED) break;
254       keyboard_events.push_back({EV_KEY, code, 1});
255       break;
256     }
257     case SDL_KEYUP: {
258       const auto code = KeycodeConverter::convert(event.key.keysym.scancode);
259       if (code == KEY_RESERVED) break;
260       keyboard_events.push_back({EV_KEY, code, 0});
261       break;
262     }
263     // Touch screen
264     case SDL_FINGERDOWN: {
265       if (!calculate_touch_coordinates(event, x, y))
266         break;
267       push_finger_down(x, y, event.tfinger.fingerId, touch_events);
268
269       break;
270     }
271     case SDL_FINGERUP: {
272       push_finger_up(event.tfinger.fingerId, touch_events);
273       break;
274     }
275         case SDL_FINGERMOTION: {
276
277       if (!calculate_touch_coordinates(event, x, y))
278         break;
279       push_finger_motion(x, y, event.tfinger.fingerId, touch_events);
280       break;
281     }
282     default:
283       break;
284   }
285
286   if (mouse_events.size() > 0) {
287     mouse_events.push_back({EV_SYN, SYN_REPORT, 0});      
288     pointer_->send_events(mouse_events);
289   }
290
291   if (keyboard_events.size() > 0)
292     keyboard_->send_events(keyboard_events);
293
294   if (touch_events.size() > 0)
295     touch_->send_events(touch_events);
296 }
297
298 int Platform::find_touch_slot(int id){
299     for (int i = 0; i < MAX_FINGERS; i++) {
300         if (touch_slots[i] == id)
301           return i;
302     }
303     return -1;
304 }
305
306 void Platform::push_slot(std::vector<input::Event> &touch_events, int slot){
307     if (last_slot != slot) {
308         touch_events.push_back({EV_ABS, ABS_MT_SLOT, slot});
309         last_slot = slot;
310     }
311 }
312
313 void Platform::push_finger_down(int x, int y, int finger_id, std::vector<input::Event> &touch_events){
314     int slot = find_touch_slot(-1);
315     if (slot == -1) {
316         DEBUG("no free slot!");
317         return;
318     }
319     touch_slots[slot] = finger_id;
320     push_slot(touch_events, slot);
321     touch_events.push_back({EV_ABS, ABS_MT_TRACKING_ID, static_cast<std::int32_t>(finger_id % MAX_TRACKING_ID + 1)});
322     touch_events.push_back({EV_ABS, ABS_MT_POSITION_X, x});
323     touch_events.push_back({EV_ABS, ABS_MT_POSITION_Y, y});
324     touch_events.push_back({EV_SYN, SYN_REPORT, 0});
325 }
326
327 void Platform::push_finger_up(int finger_id, std::vector<input::Event> &touch_events){
328     int slot = find_touch_slot(finger_id);
329     if (slot == -1) 
330       return;
331     push_slot(touch_events, slot);
332     touch_events.push_back({EV_ABS, ABS_MT_TRACKING_ID, -1});
333     touch_events.push_back({EV_SYN, SYN_REPORT, 0});
334     touch_slots[slot] = -1;
335 }
336
337 void Platform::push_finger_motion(int x, int y, int finger_id, std::vector<input::Event> &touch_events){
338     int slot = find_touch_slot(finger_id);
339     if (slot == -1) 
340       return;
341     push_slot(touch_events, slot);
342     touch_events.push_back({EV_ABS, ABS_MT_POSITION_X, x});
343     touch_events.push_back({EV_ABS, ABS_MT_POSITION_Y, y});
344     touch_events.push_back({EV_SYN, SYN_REPORT, 0});
345 }
346
347
348 bool Platform::adjust_coordinates(std::int32_t &x, std::int32_t &y) {
349   SDL_Window *window = nullptr;
350
351   if (!config_.single_window) {
352     window = SDL_GetWindowFromID(focused_sdl_window_id_);
353     return adjust_coordinates(window, x, y);
354   } else {
355     // When running the whole Android system in a single window we don't
356     // need to reacalculate and the pointer position as they are already
357     // relative to our window.
358     return true;
359   }
360 }
361
362 bool Platform::adjust_coordinates(SDL_Window *window, std::int32_t &x, std::int32_t &y) {
363   std::int32_t rel_x = 0;
364   std::int32_t rel_y = 0;
365
366   if (!window) {
367     return false;
368   }
369   // As we get only absolute coordindates relative to our window we have to
370   // calculate the correct position based on the current focused window
371   SDL_GetWindowPosition(window, &rel_x, &rel_y);
372   x += rel_x;
373   y += rel_y;
374   return true;
375 }
376
377 bool Platform::calculate_touch_coordinates(const SDL_Event &event,
378                                            std::int32_t &x,
379                                            std::int32_t &y) {
380   SDL_Window *window = nullptr;
381
382   window = SDL_GetWindowFromID(focused_sdl_window_id_);
383   // before SDL 2.0.7 on X11 tfinger coordinates are not normalized
384   if (!SDL_VERSION_ATLEAST(2,0,7) && (event.tfinger.x > 1 || event.tfinger.y > 1)) {
385     x = event.tfinger.x;
386     y = event.tfinger.y;
387   } else {
388     if (window) {
389       SDL_GetWindowSize(window, &x, &y);
390       x *= event.tfinger.x;
391       y *= event.tfinger.y;
392     } else {
393       x = display_frame_.width() * event.tfinger.x;
394       y = display_frame_.height() * event.tfinger.y;
395     }
396   }
397
398   if (config_.single_window) {
399     // When running the whole Android system in a single window we don't
400     // need to reacalculate and the pointer position as they are already
401     // relative to our window.
402     return true;
403   } else {
404     return adjust_coordinates(window, x, y);
405   }
406 }
407
408 Window::Id Platform::next_window_id() {
409   static Window::Id next_id = 0;
410   return next_id++;
411 }
412
413 std::shared_ptr<wm::Window> Platform::create_window(
414     const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame, const std::string &title) {
415   if (!renderer_) {
416     ERROR("Can't create window without a renderer set");
417     return nullptr;
418   }
419
420   auto id = next_window_id();
421   auto w = std::make_shared<Window>(renderer_, id, task, shared_from_this(), frame, title,
422                   !window_size_immutable_, !config_.server_side_decoration);
423   focused_sdl_window_id_ = w->window_id();
424   windows_.insert({id, w});
425   return w;
426 }
427
428 void Platform::window_deleted(const Window::Id &id) {
429   auto w = windows_.find(id);
430   if (w == windows_.end()) {
431     WARNING("Got window removed event for unknown window (id %d)", id);
432     return;
433   }
434   if (auto window = w->second.lock())
435     window_manager_->remove_task(window->task());
436   windows_.erase(w);
437 }
438
439 void Platform::window_wants_focus(const Window::Id &id) {
440   auto w = windows_.find(id);
441   if (w == windows_.end()) return;
442
443   if (auto window = w->second.lock()) {
444     focused_sdl_window_id_ = window->window_id();
445     window_manager_->set_focused_task(window->task());
446   }
447 }
448
449 void Platform::window_moved(const Window::Id &id, const std::int32_t &x,
450                                   const std::int32_t &y) {
451   auto w = windows_.find(id);
452   if (w == windows_.end()) return;
453
454   if (auto window = w->second.lock()) {
455     auto new_frame = window->frame();
456     new_frame.translate(x, y);
457     window->update_frame(new_frame);
458     window_manager_->resize_task(window->task(), new_frame, 3);
459   }
460 }
461
462 void Platform::window_resized(const Window::Id &id,
463                                     const std::int32_t &width,
464                                     const std::int32_t &height) {
465   auto w = windows_.find(id);
466   if (w == windows_.end()) return;
467
468   if (auto window = w->second.lock()) {
469     auto new_frame = window->frame();
470     new_frame.resize(width, height);
471     // We need to update the window frame in advance here as otherwise we may
472     // get a movement event before we got an update of the actual layer
473     // representing this window and then we're back to the original size of
474     // the task.
475     window->update_frame(new_frame);
476     window_manager_->resize_task(window->task(), new_frame, 3);
477   }
478 }
479
480 void Platform::set_clipboard_data(const ClipboardData &data) {
481   if (data.text.empty())
482     return;
483   SDL_SetClipboardText(data.text.c_str());
484 }
485
486 Platform::ClipboardData Platform::get_clipboard_data() {
487   if (!SDL_HasClipboardText())
488     return ClipboardData{};
489
490   auto text = SDL_GetClipboardText();
491   if (!text)
492     return ClipboardData{};
493
494   auto data = ClipboardData{text};
495   SDL_free(text);
496   return data;
497 }
498
499 std::shared_ptr<audio::Sink> Platform::create_audio_sink() {
500   return std::make_shared<AudioSink>();
501 }
502
503 std::shared_ptr<audio::Source> Platform::create_audio_source() {
504   ERROR("Not implemented");
505   return nullptr;
506 }
507
508 bool Platform::supports_multi_window() const {
509   return true;
510 }
511 } // namespace sdl
512 } // namespace platform
513 } // namespace anbox