| // |
| // Copyright (c) 2015 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // Copyright (c) 2023 BlackBerry Limited |
| // |
| |
| // QNXWindow.cpp: Implementation of OSWindow for QNX |
| |
| #include "qnx/QNXWindow.h" |
| |
| #include <assert.h> |
| #include <pthread.h> |
| #include <sys/keycodes.h> |
| |
| #include "aemu/base/system/System.h" |
| |
| namespace { |
| static pthread_once_t once_control = PTHREAD_ONCE_INIT; |
| static screen_context_t g_screen_ctx; |
| static int FAKE_MOUSE_BUTTON_FOR_TRIGGER_TEST_EVENT = 5; |
| |
| static void screen_init(void) { |
| /* initialize the global screen context */ |
| screen_create_context(&g_screen_ctx, SCREEN_APPLICATION_CONTEXT); |
| } |
| |
| static screen_context_t get_screen_context() { |
| pthread_once(&once_control, screen_init); |
| return g_screen_ctx; |
| } |
| |
| Key QNXCodeToKey(unsigned keycode) { |
| switch (keycode) { |
| case KEYCODE_RETURN: |
| case KEYCODE_KP_ENTER: |
| return KEY_RETURN; |
| case KEYCODE_BACKSPACE: |
| return KEY_BACK; |
| case KEYCODE_DELETE: |
| return KEY_DELETE; |
| case KEYCODE_ESCAPE: |
| return KEY_ESCAPE; |
| case KEYCODE_SPACE: |
| return KEY_SPACE; |
| case KEYCODE_ZERO: |
| case KEYCODE_RIGHT_PAREN: |
| return KEY_NUM0; |
| case KEYCODE_ONE: |
| case KEYCODE_EXCLAM: |
| return KEY_NUM1; |
| case KEYCODE_TWO: |
| case KEYCODE_AT: |
| return KEY_NUM2; |
| case KEYCODE_THREE: |
| case KEYCODE_NUMBER: |
| return KEY_NUM3; |
| case KEYCODE_FOUR: |
| case KEYCODE_DOLLAR: |
| return KEY_NUM4; |
| case KEYCODE_FIVE: |
| case KEYCODE_PERCENT: |
| return KEY_NUM5; |
| case KEYCODE_SIX: |
| case KEYCODE_CIRCUMFLEX: |
| return KEY_NUM6; |
| case KEYCODE_SEVEN: |
| case KEYCODE_AMPERSAND: |
| return KEY_NUM7; |
| case KEYCODE_EIGHT: |
| case KEYCODE_ASTERISK: |
| return KEY_NUM8; |
| case KEYCODE_NINE: |
| case KEYCODE_LEFT_PAREN: |
| return KEY_NUM9; |
| case KEYCODE_A: |
| case KEYCODE_CAPITAL_A: |
| return KEY_A; |
| case KEYCODE_B: |
| case KEYCODE_CAPITAL_B: |
| return KEY_B; |
| case KEYCODE_C: |
| case KEYCODE_CAPITAL_C: |
| return KEY_C; |
| case KEYCODE_D: |
| case KEYCODE_CAPITAL_D: |
| return KEY_D; |
| case KEYCODE_E: |
| case KEYCODE_CAPITAL_E: |
| return KEY_E; |
| case KEYCODE_F: |
| case KEYCODE_CAPITAL_F: |
| return KEY_F; |
| case KEYCODE_G: |
| case KEYCODE_CAPITAL_G: |
| return KEY_G; |
| case KEYCODE_H: |
| case KEYCODE_CAPITAL_H: |
| return KEY_H; |
| case KEYCODE_I: |
| case KEYCODE_CAPITAL_I: |
| return KEY_I; |
| case KEYCODE_J: |
| case KEYCODE_CAPITAL_J: |
| return KEY_J; |
| case KEYCODE_K: |
| case KEYCODE_CAPITAL_K: |
| return KEY_K; |
| case KEYCODE_L: |
| case KEYCODE_CAPITAL_L: |
| return KEY_L; |
| case KEYCODE_M: |
| case KEYCODE_CAPITAL_M: |
| return KEY_M; |
| case KEYCODE_N: |
| case KEYCODE_CAPITAL_N: |
| return KEY_N; |
| case KEYCODE_O: |
| case KEYCODE_CAPITAL_O: |
| return KEY_O; |
| case KEYCODE_P: |
| case KEYCODE_CAPITAL_P: |
| return KEY_P; |
| case KEYCODE_Q: |
| case KEYCODE_CAPITAL_Q: |
| return KEY_Q; |
| case KEYCODE_R: |
| case KEYCODE_CAPITAL_R: |
| return KEY_R; |
| case KEYCODE_S: |
| case KEYCODE_CAPITAL_S: |
| return KEY_S; |
| case KEYCODE_T: |
| case KEYCODE_CAPITAL_T: |
| return KEY_T; |
| case KEYCODE_U: |
| case KEYCODE_CAPITAL_U: |
| return KEY_U; |
| case KEYCODE_V: |
| case KEYCODE_CAPITAL_V: |
| return KEY_V; |
| case KEYCODE_W: |
| case KEYCODE_CAPITAL_W: |
| return KEY_W; |
| case KEYCODE_X: |
| case KEYCODE_CAPITAL_X: |
| return KEY_X; |
| case KEYCODE_Y: |
| case KEYCODE_CAPITAL_Y: |
| return KEY_Y; |
| case KEYCODE_Z: |
| case KEYCODE_CAPITAL_Z: |
| return KEY_Z; |
| case KEYCODE_PLUS: |
| case KEYCODE_EQUAL: |
| return KEY_EQUAL; |
| case KEYCODE_MINUS: |
| case KEYCODE_UNDERSCORE: |
| return KEY_SUBTRACT; |
| case KEYCODE_LESS_THAN: |
| case KEYCODE_COMMA: |
| return KEY_COMMA; |
| case KEYCODE_GREATER_THAN: |
| case KEYCODE_PERIOD: |
| return KEY_PERIOD; |
| case KEYCODE_COLON: |
| case KEYCODE_SEMICOLON: |
| return KEY_SEMICOLON; |
| case KEYCODE_SLASH: |
| case KEYCODE_QUESTION: |
| return KEY_SLASH; |
| case KEYCODE_TILDE: |
| case KEYCODE_GRAVE: |
| return KEY_TILDE; |
| case KEYCODE_LEFT_BRACE: |
| case KEYCODE_LEFT_BRACKET: |
| return KEY_LBRACKET; |
| case KEYCODE_BAR: |
| case KEYCODE_BACK_SLASH: |
| return KEY_BACKSLASH; |
| case KEYCODE_RIGHT_BRACE: |
| case KEYCODE_RIGHT_BRACKET: |
| return KEY_RBRACKET; |
| case KEYCODE_QUOTE: |
| case KEYCODE_APOSTROPHE: |
| return KEY_QUOTE; |
| case KEYCODE_PAUSE: |
| return KEY_PAUSE; |
| case KEYCODE_TAB: |
| case KEYCODE_BACK_TAB: |
| return KEY_TAB; |
| case KEYCODE_LEFT: |
| return KEY_LEFT; |
| case KEYCODE_KP_LEFT: |
| return KEY_NUMPAD4; |
| case KEYCODE_KP_FIVE: |
| return KEY_NUMPAD5; |
| case KEYCODE_RIGHT: |
| return KEY_RIGHT; |
| case KEYCODE_KP_RIGHT: |
| return KEY_NUMPAD6; |
| case KEYCODE_UP: |
| return KEY_UP; |
| case KEYCODE_KP_UP: |
| return KEY_NUMPAD8; |
| case KEYCODE_DOWN: |
| return (Key)(KEY_UP + 1); // avoid name collision |
| case KEYCODE_KP_DOWN: |
| return KEY_NUMPAD2; |
| case KEYCODE_MENU: |
| case KEYCODE_LEFT_ALT: |
| case KEYCODE_RIGHT_ALT: |
| return KEY_MENU; |
| case KEYCODE_HOME: |
| return KEY_HOME; |
| case KEYCODE_END: |
| return KEY_END; |
| case KEYCODE_LEFT_SHIFT: |
| return KEY_LSHIFT; |
| case KEYCODE_RIGHT_SHIFT: |
| return KEY_RSHIFT; |
| case KEYCODE_LEFT_CTRL: |
| return KEY_LCONTROL; |
| case KEYCODE_RIGHT_CTRL: |
| return KEY_RCONTROL; |
| case KEYCODE_KP_PLUS: |
| return KEY_ADD; |
| case KEYCODE_KP_MINUS: |
| return KEY_SUBTRACT; |
| case KEYCODE_KP_MULTIPLY: |
| return KEY_MULTIPLY; |
| case KEYCODE_KP_DIVIDE: |
| return KEY_DIVIDE; |
| case KEYCODE_F1: |
| return KEY_F1; |
| case KEYCODE_F2: |
| return KEY_F2; |
| case KEYCODE_F3: |
| return KEY_F3; |
| case KEYCODE_F4: |
| return KEY_F4; |
| case KEYCODE_F5: |
| return KEY_F5; |
| case KEYCODE_F6: |
| return KEY_F6; |
| case KEYCODE_F7: |
| return KEY_F7; |
| case KEYCODE_F8: |
| return KEY_F8; |
| case KEYCODE_F9: |
| return KEY_F9; |
| case KEYCODE_F10: |
| return KEY_F10; |
| case KEYCODE_F11: |
| return KEY_F11; |
| case KEYCODE_F12: |
| return KEY_F12; |
| default: |
| return KEY_UNKNOWN; |
| } |
| return KEY_UNKNOWN; |
| } |
| |
| MouseButton QNXCodeToButton(int button) { |
| MouseButton target_button = MOUSEBUTTON_UNKNOWN; |
| switch (button) { |
| case SCREEN_LEFT_MOUSE_BUTTON: |
| target_button = MOUSEBUTTON_LEFT; |
| break; |
| case SCREEN_MIDDLE_MOUSE_BUTTON: |
| target_button = MOUSEBUTTON_MIDDLE; |
| break; |
| case SCREEN_RIGHT_MOUSE_BUTTON: |
| target_button = MOUSEBUTTON_RIGHT; |
| break; |
| default: |
| break; |
| } |
| return target_button; |
| } |
| } // namespace |
| |
| QNXWindow::QNXWindow() : mWindow(0), mVisible(false), mPid(getpid()) {} |
| |
| QNXWindow::~QNXWindow() { destroy(); } |
| |
| bool QNXWindow::initialize(const std::string& name, size_t width, size_t height) { |
| screen_context_t screen_ctx; |
| screen_window_t screen_window; |
| int rc; |
| |
| screen_ctx = get_screen_context(); |
| if (screen_ctx == nullptr) { |
| perror("No screen context"); |
| return false; |
| } |
| |
| rc = screen_create_window_type(&screen_window, screen_ctx, SCREEN_APPLICATION_WINDOW); |
| if (rc) { |
| perror("screen_create_window"); |
| return false; |
| } |
| |
| int alpha_mode = SCREEN_PRE_MULTIPLIED_ALPHA; |
| screen_set_window_property_iv(screen_window, SCREEN_PROPERTY_ALPHA_MODE, &alpha_mode); |
| |
| int usage = SCREEN_USAGE_NATIVE | SCREEN_USAGE_OPENGL_ES2; |
| screen_set_window_property_iv(screen_window, SCREEN_PROPERTY_USAGE, &usage); |
| |
| int interval = 1; |
| screen_set_window_property_iv(screen_window, SCREEN_PROPERTY_SWAP_INTERVAL, &interval); |
| |
| int format = SCREEN_FORMAT_RGBA8888; |
| screen_set_window_property_iv(screen_window, SCREEN_PROPERTY_FORMAT, &format); |
| |
| int transp = SCREEN_TRANSPARENCY_NONE; |
| screen_set_window_property_iv(screen_window, SCREEN_PROPERTY_TRANSPARENCY, &transp); |
| |
| int pos[2] = {0, 0}; |
| screen_set_window_property_iv(screen_window, SCREEN_PROPERTY_POSITION, pos); |
| |
| int size[2] = {static_cast<int>(width), static_cast<int>(height)}; |
| screen_set_window_property_iv(screen_window, SCREEN_PROPERTY_SIZE, size); |
| |
| size[0] = width; |
| size[1] = height; |
| screen_set_window_property_iv(screen_window, SCREEN_PROPERTY_BUFFER_SIZE, size); |
| |
| rc = screen_create_window_buffers(screen_window, 2); |
| if (rc) { |
| perror("screen_create_window_buffers"); |
| screen_destroy_window(screen_window); |
| return false; |
| } |
| |
| char group_name[] = "gfx"; |
| rc = screen_create_window_group(screen_window, group_name); |
| if (rc) { |
| perror("screen_create_window_group"); |
| screen_destroy_window(screen_window); |
| return false; |
| } |
| |
| mWindow = screen_window; |
| |
| int sensitivity = SCREEN_SENSITIVITY_TEST; |
| screen_set_window_property_iv(screen_window, SCREEN_PROPERTY_SENSITIVITY, &sensitivity); |
| |
| screen_set_window_property_cv(screen_window, SCREEN_PROPERTY_ID_STRING, name.size(), |
| name.c_str()); |
| |
| int visible = mVisible ? 1 : 0; |
| screen_set_window_property_iv(screen_window, SCREEN_PROPERTY_VISIBLE, &visible); |
| |
| rc = screen_flush_context(screen_ctx, SCREEN_WAIT_IDLE); |
| if (rc) { |
| perror("flush"); |
| } |
| |
| return true; |
| } |
| |
| void QNXWindow::destroy() { |
| if (mWindow) screen_destroy_window(mWindow); |
| } |
| |
| EGLNativeWindowType QNXWindow::getNativeWindow() const { return mWindow; } |
| |
| EGLNativeDisplayType QNXWindow::getNativeDisplay() const { |
| // TODO: yodai |
| return 0; |
| } |
| |
| void* QNXWindow::getFramebufferNativeWindow() const { return mWindow; } |
| |
| void QNXWindow::messageLoop() { |
| screen_event_t screen_event; |
| int rc = screen_create_event(&screen_event); |
| if (rc) { |
| perror("screen_create_event"); |
| return; |
| } |
| |
| while (!screen_get_event(get_screen_context(), screen_event, 0)) { |
| int event_type; |
| rc = screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &event_type); |
| if (rc || event_type == SCREEN_EVENT_NONE) { |
| break; |
| } |
| |
| switch (event_type) { |
| case SCREEN_EVENT_KEYBOARD: |
| processKeyEvent(screen_event); |
| break; |
| case SCREEN_EVENT_POINTER: |
| processMouseEvent(screen_event); |
| break; |
| case SCREEN_EVENT_PROPERTY: |
| processPropertyChangedEvent(screen_event); |
| break; |
| case SCREEN_EVENT_INPUT_CONTROL: |
| processInputControlEvent(screen_event); |
| break; |
| case SCREEN_EVENT_CLOSE: |
| processCloseEvent(screen_event); |
| break; |
| default: |
| break; |
| } |
| } |
| screen_destroy_event(screen_event); |
| } |
| |
| void QNXWindow::setMousePosition(int x, int y) { |
| screen_event_t screen_event; |
| if (screen_create_event(&screen_event)) return; |
| |
| int param = SCREEN_EVENT_POINTER; |
| if (screen_set_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, ¶m)) return; |
| |
| if (screen_set_event_property_pv(screen_event, SCREEN_PROPERTY_WINDOW, (void**)&mWindow)) |
| return; |
| int coords[] = {x, y}; |
| screen_set_event_property_iv(screen_event, SCREEN_PROPERTY_SOURCE_POSITION, coords); |
| screen_send_event(get_screen_context(), screen_event, mPid); |
| } |
| |
| OSWindow* CreateOSWindow() { return new QNXWindow(); } |
| |
| bool QNXWindow::setPosition(int x, int y) { |
| int pos[2] = {x, y}; |
| screen_set_window_property_iv(mWindow, SCREEN_PROPERTY_POSITION, pos); |
| screen_context_t screen_ctx = get_screen_context(); |
| screen_flush_context(screen_ctx, 0); |
| return true; |
| } |
| |
| bool QNXWindow::resize(int width, int height) { |
| int size[2] = {width, height}; |
| screen_set_window_property_iv(mWindow, SCREEN_PROPERTY_SIZE, size); |
| size[0] = width; |
| size[1] = height; |
| screen_set_window_property_iv(mWindow, SCREEN_PROPERTY_BUFFER_SIZE, size); |
| return true; |
| } |
| |
| void QNXWindow::setVisible(bool isVisible) { |
| if (mVisible == isVisible) { |
| return; |
| } |
| int visible = isVisible ? 1 : 0; |
| if (!screen_set_window_property_iv(mWindow, SCREEN_PROPERTY_VISIBLE, &visible)) |
| mVisible = isVisible; |
| } |
| |
| void QNXWindow::signalTestEvent() { |
| screen_event_t screen_event; |
| if (screen_create_event(&screen_event)) return; |
| |
| int param = SCREEN_EVENT_POINTER; |
| if (screen_set_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, ¶m)) return; |
| |
| if (screen_set_event_property_pv(screen_event, SCREEN_PROPERTY_WINDOW, (void**)&mWindow)) |
| return; |
| int button = FAKE_MOUSE_BUTTON_FOR_TRIGGER_TEST_EVENT; |
| screen_set_event_property_iv(screen_event, SCREEN_PROPERTY_BUTTONS, &button); |
| screen_send_event(get_screen_context(), screen_event, mPid); |
| } |
| |
| void QNXWindow::processMouseEvent(const screen_event_t& screen_event) { |
| static int s_lastButtonState = 0; |
| int buttonState = 0; |
| screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_BUTTONS, &buttonState); |
| Event event; |
| if (buttonState == FAKE_MOUSE_BUTTON_FOR_TRIGGER_TEST_EVENT) { |
| event.Type = Event::EVENT_TEST; |
| pushEvent(event); |
| return; |
| } |
| |
| int lastButtonState = s_lastButtonState; |
| |
| s_lastButtonState = buttonState; |
| int wheel_ticks = 0; |
| screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_MOUSE_WHEEL, &wheel_ticks); |
| if (wheel_ticks) |
| event.Type = Event::EVENT_MOUSE_WHEEL_MOVED; |
| else if (buttonState == lastButtonState) |
| event.Type = Event::EVENT_MOUSE_MOVED; |
| else if (buttonState > lastButtonState) |
| event.Type = Event::EVENT_MOUSE_BUTTON_PRESSED; |
| else |
| event.Type = Event::EVENT_MOUSE_BUTTON_RELEASED; |
| |
| switch (event.Type) { |
| case Event::EVENT_MOUSE_WHEEL_MOVED: |
| event.MouseWheel.Delta = wheel_ticks; |
| pushEvent(event); |
| break; |
| case Event::EVENT_MOUSE_MOVED: |
| int position[2]; |
| screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_SOURCE_POSITION, position); |
| event.MouseMove.X = position[0]; |
| event.MouseMove.Y = position[1]; |
| pushEvent(event); |
| break; |
| case Event::EVENT_MOUSE_BUTTON_RELEASED: |
| case Event::EVENT_MOUSE_BUTTON_PRESSED: |
| event.MouseButton.Button = event.Type == Event::EVENT_MOUSE_BUTTON_RELEASED |
| ? QNXCodeToButton(lastButtonState) |
| : QNXCodeToButton(buttonState); |
| if (event.MouseButton.Button != MOUSEBUTTON_UNKNOWN) { |
| // int position[2]; |
| screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_SOURCE_POSITION, |
| position); |
| event.MouseButton.X = position[0]; |
| event.MouseButton.Y = position[1]; |
| pushEvent(event); |
| } |
| break; |
| } |
| } |
| |
| void QNXWindow::processKeyEvent(const screen_event_t& screen_event) { |
| int modifiers = 0; |
| int flags = 0; |
| int cap = 0; |
| |
| screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_MODIFIERS, &modifiers); |
| screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_FLAGS, &flags); |
| screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_KEY_CAP, &cap); |
| |
| Event event; |
| event.Key.Code = QNXCodeToKey(cap); |
| event.Type = flags & KEY_DOWN || flags & KEY_REPEAT ? Event::EVENT_KEY_PRESSED |
| : Event::EVENT_KEY_RELEASED; |
| event.Key.Shift = modifiers & KEYMOD_SHIFT; |
| event.Key.Control = modifiers & KEYMOD_CTRL; |
| event.Key.Alt = modifiers & KEYMOD_ALT; |
| event.Key.System = 0; |
| pushEvent(event); |
| } |
| |
| void QNXWindow::processPropertyChangedEvent(const screen_event_t& event) { |
| int property = 0; |
| screen_get_event_property_iv(event, SCREEN_PROPERTY_NAME, &property); |
| int type = 0; |
| screen_get_event_property_iv(event, SCREEN_PROPERTY_OBJECT_TYPE, &type); |
| if (type != SCREEN_OBJECT_TYPE_WINDOW) return; |
| |
| if (property == SCREEN_PROPERTY_SIZE) { |
| screen_window_t screenWindow = 0; |
| if (screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, (void**)&screenWindow)) |
| return; |
| if (screenWindow != mWindow) return; |
| |
| int size[2] = {0, 0}; |
| if (screen_get_window_property_iv(screenWindow, SCREEN_PROPERTY_SIZE, size)) return; |
| Event event; |
| event.Type = Event::EVENT_RESIZED; |
| event.Size.Width = size[0]; |
| event.Size.Height = size[1]; |
| pushEvent(event); |
| } else if (property == SCREEN_PROPERTY_FOCUS) { |
| screen_window_t screenWindow = 0; |
| if (screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, (void**)&screenWindow)) |
| return; |
| if (screenWindow != mWindow) return; |
| int value; |
| if (screen_get_window_property_iv(screenWindow, property, &value)) return; |
| Event event; |
| event.Type = value ? Event::EVENT_GAINED_FOCUS : Event::EVENT_LOST_FOCUS; |
| } |
| } |
| |
| void QNXWindow::processInputControlEvent(const screen_event_t& screen_event) { |
| int val; |
| if (screen_get_event_property_iv(screen_event, SCREEN_INPUT_CONTROL_POINTER_STOP, &val)) return; |
| if (!val) return; |
| screen_window_t screenWindow = 0; |
| if (screen_get_event_property_pv(screen_event, SCREEN_PROPERTY_WINDOW, (void**)&screenWindow)) |
| return; |
| if (screenWindow != mWindow) return; |
| Event event; |
| event.Type = Event::EVENT_MOUSE_LEFT; |
| pushEvent(event); |
| } |
| |
| void QNXWindow::processCloseEvent(const screen_event_t& screen_event) { |
| screen_window_t screenWindow = 0; |
| if (screen_get_event_property_pv(screen_event, SCREEN_PROPERTY_WINDOW, (void**)&screenWindow)) |
| return; |
| if (screenWindow != mWindow) return; |
| Event event; |
| event.Type = Event::EVENT_CLOSED; |
| pushEvent(event); |
| } |