/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved. * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup GHOST */ #include "GHOST_System.hh" #include "GHOST_EventManager.hh" #include "GHOST_TimerManager.hh" #include "GHOST_TimerTask.hh" #include "GHOST_WindowManager.hh" #ifdef WITH_INPUT_NDOF # include "GHOST_NDOFManager.hh" #endif GHOST_System::GHOST_System() : native_pixel_(false), window_focus_(true), auto_focus_(true), window_csd_params_({nullptr}), window_csd_layout_({0}), timer_manager_(nullptr), window_manager_(nullptr), event_manager_(nullptr), #ifdef WITH_INPUT_NDOF ndof_manager_(nullptr), #endif multitouch_gestures_(true), tablet_api_(GHOST_kTabletAutomatic), is_debug_enabled_(false) { } GHOST_System::~GHOST_System() { exit(); } GHOST_TSuccess GHOST_System::hasClipboardImage() const { return GHOST_kFailure; } uint *GHOST_System::getClipboardImage(int * /*r_width*/, int * /*r_height*/) const { return nullptr; } GHOST_TSuccess GHOST_System::putClipboardImage(uint * /*rgba*/, int /*width*/, int /*height*/) const { return GHOST_kFailure; } void GHOST_System::setWindowCSD(const GHOST_CSD_Params ¶ms) { window_csd_params_ = params; } const GHOST_CSD_Params &GHOST_System::getWindowCSD() const { return window_csd_params_; } const GHOST_CSD_Layout &GHOST_System::getWindowCSD_Layout() const { return window_csd_layout_; } void GHOST_System::setWindowCSD_Layout(const GHOST_CSD_Layout &layout) { window_csd_layout_ = layout; } void GHOST_System::setIconGenerator(const GHOST_IconGenerator *icon_generator) { icon_generator_ = icon_generator; } GHOST_ITimerTask *GHOST_System::installTimer(uint64_t delay, uint64_t interval, GHOST_TimerProcPtr timer_proc, GHOST_TUserDataPtr user_data) { uint64_t millis = getMilliSeconds(); GHOST_TimerTask *timer = new GHOST_TimerTask(millis + delay, interval, timer_proc, user_data); if (timer) { if (timer_manager_->addTimer(timer) == GHOST_kSuccess) { /* Check to see whether we need to fire the timer right away. */ timer_manager_->fireTimers(millis); } else { delete timer; timer = nullptr; } } return timer; } GHOST_TSuccess GHOST_System::removeTimer(GHOST_ITimerTask *timerTask) { GHOST_TSuccess success = GHOST_kFailure; if (timerTask) { success = timer_manager_->removeTimer((GHOST_TimerTask *)timerTask); } return success; } GHOST_TSuccess GHOST_System::disposeWindow(GHOST_IWindow *window) { GHOST_TSuccess success; /* * Remove all pending events for the window. */ if (window_manager_->getWindowFound(window)) { event_manager_->removeWindowEvents(window); success = window_manager_->removeWindow(window); if (success) { delete window; } } else { success = GHOST_kFailure; } return success; } bool GHOST_System::validWindow(GHOST_IWindow *window) { return window_manager_->getWindowFound(window); } GHOST_IWindow *GHOST_System::getWindowUnderCursor(int32_t x, int32_t y) { /* TODO: This solution should follow the order of the activated windows (Z-order). * It is imperfect but usable in most cases. Ideally each platform should provide * a custom version of this function that properly considers z-order. */ std::vector windows = window_manager_->getWindows(); std::vector::reverse_iterator iwindow_iter; /* Search through the windows in reverse order because in most cases * the window that is on top was created after those that are below it. */ for (iwindow_iter = windows.rbegin(); iwindow_iter != windows.rend(); ++iwindow_iter) { GHOST_IWindow *win = *iwindow_iter; if (win->getState() == GHOST_kWindowStateMinimized) { continue; } GHOST_Rect bounds; win->getClientBounds(bounds); if (bounds.isInside(x, y)) { return win; } } return nullptr; } void GHOST_System::dispatchEvents() { #ifdef WITH_INPUT_NDOF /* NDOF Motion event is sent only once per dispatch, so do it now: */ if (ndof_manager_) { ndof_manager_->sendMotionEvent(); } #endif if (event_manager_) { event_manager_->dispatchEvents(); } timer_manager_->fireTimers(getMilliSeconds()); } GHOST_TSuccess GHOST_System::addEventConsumer(GHOST_IEventConsumer *consumer) { GHOST_TSuccess success; if (event_manager_) { success = event_manager_->addConsumer(consumer); } else { success = GHOST_kFailure; } return success; } GHOST_TSuccess GHOST_System::removeEventConsumer(GHOST_IEventConsumer *consumer) { GHOST_TSuccess success; if (event_manager_) { success = event_manager_->removeConsumer(consumer); } else { success = GHOST_kFailure; } return success; } GHOST_TSuccess GHOST_System::pushEvent(std::unique_ptr event) { GHOST_TSuccess success; if (event_manager_) { success = event_manager_->pushEvent(std::move(event)); } else { success = GHOST_kFailure; } return success; } GHOST_TSuccess GHOST_System::getCursorPositionClientRelative(const GHOST_IWindow *window, int32_t &x, int32_t &y) const { /* Sub-classes that can implement this directly should do so. */ int32_t screen_x, screen_y; GHOST_TSuccess success = getCursorPosition(screen_x, screen_y); if (success == GHOST_kSuccess) { window->screenToClient(screen_x, screen_y, x, y); } return success; } GHOST_TSuccess GHOST_System::setCursorPositionClientRelative(GHOST_IWindow *window, int32_t x, int32_t y) { /* Sub-classes that can implement this directly should do so. */ int32_t screen_x, screen_y; window->clientToScreen(x, y, screen_x, screen_y); return setCursorPosition(screen_x, screen_y); } uint32_t GHOST_System::getCursorPreferredLogicalSize() const { return uint32_t(24); } GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKey mask, bool &is_down) const { is_down = false; GHOST_ModifierKeys keys; /* Get the state of all modifier keys. */ GHOST_TSuccess success = getModifierKeys(keys); if (success) { /* Isolate the state of the key requested. */ is_down = keys.get(mask); } return success; } GHOST_TSuccess GHOST_System::getButtonState(GHOST_TButton mask, bool &is_down) const { is_down = false; GHOST_Buttons buttons; /* Get the state of all mouse buttons. */ GHOST_TSuccess success = getButtons(buttons); if (success) { /* Isolate the state of the mouse button requested. */ is_down = buttons.get(mask); } return success; } void GHOST_System::setMultitouchGestures(const bool use) { multitouch_gestures_ = use; } void GHOST_System::setTabletAPI(GHOST_TTabletAPI api) { tablet_api_ = api; } GHOST_TTabletAPI GHOST_System::getTabletAPI() { return tablet_api_; } GHOST_TSuccess GHOST_System::getPixelAtCursor(float /*r_color*/[3]) const { return GHOST_kFailure; } #ifdef WITH_INPUT_NDOF void GHOST_System::setNDOFDeadZone(float deadzone) { if (this->ndof_manager_) { this->ndof_manager_->setDeadZone(deadzone); } } #endif GHOST_TSuccess GHOST_System::init() { timer_manager_ = new GHOST_TimerManager(); window_manager_ = new GHOST_WindowManager(); event_manager_ = new GHOST_EventManager(); #ifdef WITH_GHOST_DEBUG if (event_manager_) { event_printer_ = new GHOST_EventPrinter(); event_manager_->addConsumer(event_printer_); } #endif /* WITH_GHOST_DEBUG */ if (timer_manager_ && window_manager_ && event_manager_) { return GHOST_kSuccess; } return GHOST_kFailure; } GHOST_TSuccess GHOST_System::exit() { /** WARNING: exit() may run more than once, since it may need to be called from a derived class * destructor. Take it into account when modifying this function. */ delete window_manager_; window_manager_ = nullptr; delete timer_manager_; timer_manager_ = nullptr; delete event_manager_; event_manager_ = nullptr; #ifdef WITH_INPUT_NDOF delete ndof_manager_; ndof_manager_ = nullptr; #endif return GHOST_kSuccess; } bool GHOST_System::useNativePixel() { native_pixel_ = true; return true; } void GHOST_System::useWindowFocus(const bool use_focus) { window_focus_ = use_focus; } void GHOST_System::setAutoFocus(const bool auto_focus) { auto_focus_ = auto_focus; } void GHOST_System::initDebug(GHOST_Debug debug) { is_debug_enabled_ = debug.flags & GHOST_kDebugDefault; } bool GHOST_System::isDebugEnabled() { return is_debug_enabled_; }