From 9b0da6ad021dcdd08eb066cdeacb8c84c491f48d Mon Sep 17 00:00:00 2001 From: zhaoyingzhen Date: Thu, 23 Jul 2026 18:57:23 +0800 Subject: [PATCH] feat(shortcut): migrate shortcut handling to dde-services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Disable the legacy session keybinding module and remove its D-Bus activation file. 2. Remove the legacy configurable touchpad gesture API while retaining touchscreen handling. 3. Make the system gesture input loop startup, shutdown, timer cleanup, and device cleanup deterministic. 4. Release emulated touchscreen right-button state on lock, configuration, service, and module lifecycle changes. Log: Migrate shortcut ownership to dde-services while preserving touchscreen gesture handling. Influence: 1. Verify shortcuts are handled by dde-services on X11 and Treeland. 2. Verify touchscreen edge gestures and long-press right-click behavior. 3. Verify restarting gesture services leaves no stuck input state. feat(shortcut): 将快捷键处理迁移到 dde-services 1. 禁用旧的会话 keybinding 模块,并删除其 D-Bus 激活文件。 2. 删除旧的可配置触控板手势接口,同时保留触摸屏手势处理。 3. 完善系统手势输入循环的启动、停止、定时器和设备清理流程。 4. 在锁屏、配置变更、服务重启和模块销毁时释放模拟的触摸屏右键状态。 Log: 将快捷键处理迁移到 dde-services,同时保留触摸屏手势处理。 Influence: 1. 验证 X11 和 Treeland 下快捷键由 dde-services 正常处理。 2. 验证触摸屏边缘手势和长按右键行为。 3. 验证手势服务重启后不会残留按键状态。 Pms: TASK-393199 Change-Id: I7d2ce001dfae491c08357e3e10943784e2ad80e3 --- bin/dde-session-daemon/daemon.go | 1 - bin/dde-session-daemon/main.go | 2 +- bin/dde-session-daemon/module.go | 1 - bin/dde-session-daemon/module_test.go | 7 +- gesture1/daemon.go | 24 +- gesture1/daemon_test.go | 36 ++ gesture1/exported_methods_auto.go | 53 -- gesture1/gesture1_dbusutil.go | 16 - gesture1/gesture_action.go | 205 +------ gesture1/gesture_info.go | 69 --- gesture1/manager.go | 422 ++++++++------- gesture1/manager_ifc.go | 132 ----- gesture1/utils.go | 9 +- .../org.deepin.dde.daemon.loader.json | 4 +- .../org.deepin.dde.Keybinding1.service | 4 - system/gesture1/core.c | 500 ++++++++++++++++-- system/gesture1/core.h | 4 +- system/gesture1/gesture.go | 63 ++- 18 files changed, 761 insertions(+), 791 deletions(-) create mode 100644 gesture1/daemon_test.go delete mode 100644 gesture1/exported_methods_auto.go delete mode 100644 gesture1/gesture1_dbusutil.go delete mode 100644 gesture1/gesture_info.go delete mode 100644 gesture1/manager_ifc.go delete mode 100644 misc/services/org.deepin.dde.Keybinding1.service diff --git a/bin/dde-session-daemon/daemon.go b/bin/dde-session-daemon/daemon.go index e40d46ea5..bd5b043b5 100644 --- a/bin/dde-session-daemon/daemon.go +++ b/bin/dde-session-daemon/daemon.go @@ -189,7 +189,6 @@ func (s *SessionDaemon) initModules() { "power", // need screensaver and sessionwatcher "service-trigger", "clipboard", - "keybinding", "inputdevices", "gesture", "housekeeping", diff --git a/bin/dde-session-daemon/main.go b/bin/dde-session-daemon/main.go index 7ac00dc36..18329e8df 100755 --- a/bin/dde-session-daemon/main.go +++ b/bin/dde-session-daemon/main.go @@ -40,7 +40,7 @@ var logger = log.NewLogger("daemon/dde-session-daemon") var hasDDECookie bool var hasTreeLand bool -var treeLandNotAllowModules = []string{"x-event-monitor", "keybinding", "gesture", "screensaver", "display", "xsettings", "power"} +var treeLandNotAllowModules = []string{"x-event-monitor", "screensaver", "display", "xsettings", "power"} func isInShutdown() bool { bus, err := dbus.SystemBus() diff --git a/bin/dde-session-daemon/module.go b/bin/dde-session-daemon/module.go index f43ca6e6a..0be7b67a6 100644 --- a/bin/dde-session-daemon/module.go +++ b/bin/dde-session-daemon/module.go @@ -23,7 +23,6 @@ import ( _ "github.com/linuxdeepin/dde-daemon/gesture1" _ "github.com/linuxdeepin/dde-daemon/housekeeping" _ "github.com/linuxdeepin/dde-daemon/inputdevices1" - _ "github.com/linuxdeepin/dde-daemon/keybinding1" _ "github.com/linuxdeepin/dde-daemon/lastore1" _ "github.com/linuxdeepin/dde-daemon/grub_gfx" diff --git a/bin/dde-session-daemon/module_test.go b/bin/dde-session-daemon/module_test.go index 8cbe1a873..c20ecae4e 100644 --- a/bin/dde-session-daemon/module_test.go +++ b/bin/dde-session-daemon/module_test.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -7,9 +7,14 @@ package main import ( "testing" + "github.com/linuxdeepin/dde-daemon/loader" "github.com/stretchr/testify/assert" ) +func TestGestureModuleRegistered(t *testing.T) { + assert.NotNil(t, loader.GetModule("gesture")) +} + func Test_isStrInList(t *testing.T) { type args struct { item string diff --git a/gesture1/daemon.go b/gesture1/daemon.go index 58e0998c9..41f3eabf7 100644 --- a/gesture1/daemon.go +++ b/gesture1/daemon.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -16,8 +16,6 @@ type Daemon struct { const ( dbusServiceName = "org.deepin.dde.Gesture1" - dbusServicePath = "/org/deepin/dde/Gesture1" - dbusServiceIFC = dbusServiceName ) var ( @@ -42,31 +40,13 @@ func (d *Daemon) Start() error { if d.manager != nil { return nil } - service := loader.GetService() var err error - d.manager, err = newManager(service) + d.manager, err = newManager() if err != nil { logger.Error("failed to initialize gesture manager:", err) return err } - err = service.Export(dbusServicePath, d.manager) - if err != nil { - logger.Error("failed to export gesture:", err) - return err - } - - err = service.RequestName(dbusServiceName) - if err != nil { - logger.Error("failed to request gesture name:", err) - d.manager.destroy() - err1 := service.StopExport(d.manager) - if err1 != nil { - logger.Error("failed to StopExport:", err1) - } - return err - } - d.manager.init() return nil diff --git a/gesture1/daemon_test.go b/gesture1/daemon_test.go new file mode 100644 index 000000000..56b660ed2 --- /dev/null +++ b/gesture1/daemon_test.go @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +package gesture1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewDaemon(t *testing.T) { + daemon := NewDaemon() + + assert.Equal(t, "gesture", daemon.Name()) +} + +func TestIsTouchRightButtonEvent(t *testing.T) { + tests := []struct { + name string + direction string + want bool + }{ + {name: "touch right button", direction: "down", want: true}, + {name: "touch right button", direction: "up", want: true}, + {name: "touch right button", direction: "left", want: false}, + {name: "swipe", direction: "up", want: false}, + } + + for _, test := range tests { + t.Run(test.name+"/"+test.direction, func(t *testing.T) { + assert.Equal(t, test.want, isTouchRightButtonEvent(test.name, test.direction)) + }) + } +} diff --git a/gesture1/exported_methods_auto.go b/gesture1/exported_methods_auto.go deleted file mode 100644 index 1c62a8e8d..000000000 --- a/gesture1/exported_methods_auto.go +++ /dev/null @@ -1,53 +0,0 @@ -// Code generated by "dbusutil-gen em -type Manager"; DO NOT EDIT. - -package gesture1 - -import ( - "github.com/linuxdeepin/go-lib/dbusutil" -) - -func (v *Manager) GetExportedMethods() dbusutil.ExportedMethods { - return dbusutil.ExportedMethods{ - { - Name: "GetEdgeMoveStopDuration", - Fn: v.GetEdgeMoveStopDuration, - OutArgs: []string{"duration"}, - }, - { - Name: "GetLongPressDuration", - Fn: v.GetLongPressDuration, - OutArgs: []string{"duration"}, - }, - { - Name: "GetShortPressDuration", - Fn: v.GetShortPressDuration, - OutArgs: []string{"duration"}, - }, - { - Name: "SetEdgeMoveStopDuration", - Fn: v.SetEdgeMoveStopDuration, - InArgs: []string{"duration"}, - }, - { - Name: "SetLongPressDuration", - Fn: v.SetLongPressDuration, - InArgs: []string{"duration"}, - }, - { - Name: "SetShortPressDuration", - Fn: v.SetShortPressDuration, - InArgs: []string{"duration"}, - }, - { - Name: "GetGestureAvaiableActions", - Fn: v.GetGestureAvaiableActions, - InArgs: []string{"name", "fingers"}, - OutArgs: []string{"actions"}, - }, - { - Name: "SetGesture", - Fn: v.SetGesture, - InArgs: []string{"name", "direction", "fingers", "action"}, - }, - } -} diff --git a/gesture1/gesture1_dbusutil.go b/gesture1/gesture1_dbusutil.go deleted file mode 100644 index 9f956157e..000000000 --- a/gesture1/gesture1_dbusutil.go +++ /dev/null @@ -1,16 +0,0 @@ -// Code generated by "dbusutil-gen -type Manager -import github.com/godbus/dbus/v5 manager.go"; DO NOT EDIT. - -package gesture1 - -func (v *Manager) setPropInfos(value GestureInfos) (changed bool) { - if value != nil { - v.Infos = value - v.emitPropChangedInfos(value) - return true - } - return false -} - -func (v *Manager) emitPropChangedInfos(value GestureInfos) error { - return v.service.EmitPropertyChanged(v, "Infos", value) -} diff --git a/gesture1/gesture_action.go b/gesture1/gesture_action.go index 26e35ea99..484e6655b 100644 --- a/gesture1/gesture_action.go +++ b/gesture1/gesture_action.go @@ -7,179 +7,14 @@ package gesture1 import ( "fmt" "os/exec" - - "github.com/linuxdeepin/go-lib/gettext" -) - -type actionInfo struct { - Name string - Description string - fn func() error -} - -type actionInfos []*actionInfo - -var actions []*actionInfo - -const ( - wmActionShowWorkspace int32 = iota + 1 - wmActionToggleMaximize - wmActionMinimize - wmActionShowWindow = 6 - wmActionShowAllWindow = 7 ) -const ( - wmTileDirectionLeft uint32 = iota + 1 - wmTileDirectionRight -) - -func (m *Manager) initActions() { - actions = []*actionInfo{ - {"MaximizeWindow", gettext.Tr("Maximize Window"), m.doMaximizeActiveWindow}, - {"RestoreWindow", gettext.Tr("Restore Window"), m.doUnMaximizeActiveWindow}, - {"SplitWindowLeft", gettext.Tr("Current Window Left Split"), m.doTileActiveWindowLeft}, - {"SplitWindowRight", gettext.Tr("Current Window Right Split"), m.doTileActiveWindowRight}, - {"ShowMultiTask", gettext.Tr("Show multitasking view"), m.doShowMultiTasking}, - {"HideMultitask", gettext.Tr("Hide multitasking view"), m.doHideMultiTasking}, - {"SwitchToPreDesktop", gettext.Tr("Switch to previous desktop"), m.doPreviousWorkspace}, - {"SwitchToNextDesktop", gettext.Tr("Switch to the next desktop"), m.doNextWorkspace}, - {"ShowDesktop", gettext.Tr("Show desktop"), m.toggleShowDesktop}, - {"HideDesktop", gettext.Tr("Hide desktop"), m.toggleShowDesktop}, - {"ToggleLaunchPad", gettext.Tr("Show/hide launcher"), m.doToggleLaunchpad}, - {"MouseRightButtonDown", gettext.Tr("Mouse right button pressed"), m.doXdotoolsMouseDown}, - {"MouseRightButtonUp", gettext.Tr("Mouse right button released"), m.doXdotoolsMouseUp}, - {"ToggleClipboard", gettext.Tr("Show/hide clipboard"), m.doToggleClipboard}, - {"ToggleGrandSearch", gettext.Tr("Show/hide grand search"), m.doToggleGrandSearch}, - {"ToggleNotifications", gettext.Tr("Show/hide notification center"), m.doToggleNotifications}, - {"Disable", gettext.Tr("Disable"), nil}, - } -} - -func (m *Manager) toggleShowDesktop() error { - return exec.Command("/usr/lib/deepin-daemon/desktop-toggle").Run() -} +const wmActionShowWorkspace int32 = 1 func (m *Manager) toggleShowMultiTasking() error { return m.wm.PerformAction(0, wmActionShowWorkspace) } -func (m *Manager) getWmStates() (bool, bool, error) { - isShowDesktop, err := m.wm.GetIsShowDesktop(0) - if err != nil { - return false, false, err - } - isShowMultiTask, err := m.wm.GetMultiTaskingStatus(0) - if err != nil { - return false, false, err - } - - return isShowDesktop, isShowMultiTask, nil -} - -func (m *Manager) doHandle4Or5FingersSwipeUp() error { - isShowDesktop, isShowMultiTask, err := m.getWmStates() - if err != nil { - return err - } - - if !isShowMultiTask { - if !isShowDesktop { - if err := m.toggleShowMultiTasking(); err != nil { - return err - } - logMultiTaskViewEvent(eventLaunchTypeTouchPad) - return nil - } - return m.toggleShowDesktop() - } - - return nil -} - -func (m *Manager) doHandle4Or5FingersSwipeDown() error { - isShowDesktop, isShowMultiTask, err := m.getWmStates() - if err != nil { - return err - } - - if isShowMultiTask { - return m.toggleShowMultiTasking() - } - if !isShowDesktop { - return m.toggleShowDesktop() - } - return nil -} - -func (m *Manager) doMaximizeActiveWindow() error { - return m.wm.MaximizeActiveWindow(0) -} - -func (m *Manager) doUnMaximizeActiveWindow() error { - return m.wm.UnMaximizeActiveWindow(0) -} - -func (m *Manager) doMinimize() error { - return m.wm.PerformAction(0, wmActionMinimize) -} - -func (m *Manager) doShowWindow() error { - return m.wm.PerformAction(0, wmActionShowWindow) -} - -func (m *Manager) doShowAllWindow() error { - return m.wm.PerformAction(0, wmActionShowAllWindow) -} - -func (m *Manager) doSwitchApplication() error { - return m.wm.SwitchApplication(0, false) -} - -func (m *Manager) doReverseSwitchApplication() error { - return m.wm.SwitchApplication(0, true) -} - -func (m *Manager) doSwitchWorkspace() error { - return m.wm.SwitchToWorkspace(0, false) -} - -func (m *Manager) doReverseSwitchWorkspace() error { - return m.wm.SwitchToWorkspace(0, true) -} - -func (m *Manager) doTileActiveWindowLeft() error { - if err := m.wm.TileActiveWindow(0, wmTileDirectionLeft); err != nil { - return err - } - logSplitScreenEvent() - return nil -} - -func (m *Manager) doTileActiveWindowRight() error { - if err := m.wm.TileActiveWindow(0, wmTileDirectionRight); err != nil { - return err - } - logSplitScreenEvent() - return nil -} - -func (m *Manager) doMoveActiveWindow() error { - return m.wm.BeginToMoveActiveWindow(0) -} - -func (m *Manager) doNextWorkspace() error { - return m.wm.NextWorkspace(0) -} - -func (m *Manager) doPreviousWorkspace() error { - return m.wm.PreviousWorkspace(0) -} - -func (m *Manager) doToggleLaunchpad() error { - return m.launchpad.Toggle(0) -} - func (m *Manager) doXdotoolsMouseDown() error { out, err := exec.Command("xdotool", "mousedown", "3").CombinedOutput() if err != nil { @@ -209,41 +44,3 @@ func (m *Manager) showMultiTaskingWithLaunchType(launchType string) error { } return nil } - -func (m *Manager) doShowMultiTasking() error { - return m.showMultiTaskingWithLaunchType(eventLaunchTypeTouchPad) -} - -func (m *Manager) doHideMultiTasking() error { - isShowMultiTask, err := m.wm.GetMultiTaskingStatus(0) - if err != nil { - return err - } - if isShowMultiTask { - return m.toggleShowMultiTasking() - } - return nil -} - -func (m *Manager) doToggleClipboard() error { - return m.clipboard.Toggle(0) -} - -func (m *Manager) doToggleGrandSearch() error { - cmd := "/usr/libexec/dde-daemon/keybinding/shortcut-dde-grand-search.sh" - return exec.Command("/bin/sh", "-c", cmd).Run() -} - -func (m *Manager) doToggleNotifications() error { - cmd := "dbus-send --print-reply --dest=org.deepin.dde.Osd1 /org/deepin/dde/shell/notification/center org.deepin.dde.shell.notification.center.Toggle" - return exec.Command("/bin/bash", "-c", cmd).Run() -} - -func doActionByName(name string) error { - for _, action := range actions { - if action.Name == name { - return action.fn() - } - } - return fmt.Errorf("action %s not found", name) -} diff --git a/gesture1/gesture_info.go b/gesture1/gesture_info.go deleted file mode 100644 index abbf535fd..000000000 --- a/gesture1/gesture_info.go +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. -// -// SPDX-License-Identifier: GPL-3.0-or-later - -package gesture1 - -import ( - "fmt" -) - -type EventInfo struct { - Name string - Direction string - Fingers int32 -} - -type GestureInfo struct { - Name string - Direction string - Fingers int32 - ActionName string -} - -type GestureInfos []*GestureInfo - -var gestureInfos = GestureInfos{ - {"swipe", "up", 3, "MaximizeWindow"}, - {"swipe", "down", 3, "RestoreWindow"}, - {"swipe", "left", 3, "SplitWindowLeft"}, - {"swipe", "right", 3, "SplitWindowRight"}, - {"tap", "none", 3, "ToggleGrandSearch"}, - {"swipe", "up", 4, "ShowMultiTask"}, - {"swipe", "down", 4, "HideMultitask"}, - {"swipe", "left", 4, "SwitchToPreDesktop"}, - {"swipe", "right", 4, "SwitchToNextDesktop"}, - {"tap", "none", 4, "ToggleLaunchPad"}, - {"touch right button", "down", 0, "MouseRightButtonDown"}, - {"touch right button", "up", 0, "MouseRightButtonUp"}, -} - -func (m *Manager) GetGestureByEvent(event EventInfo) *GestureInfo { - for _, gesture := range m.Infos { - if gesture.Name == event.Name && - gesture.Direction == event.Direction && - gesture.Fingers == event.Fingers { - return gesture - } - } - return nil -} - -func (info *GestureInfo) toString() string { - return fmt.Sprintf("Name=%s, Direction=%s, Fingers=%d action info:%v", info.Name, info.Direction, info.Fingers, info.ActionName) -} - -func (evInfo EventInfo) toString() string { - return fmt.Sprintf("Name=%s, Direction=%s, Fingers=%d", evInfo.Name, evInfo.Direction, evInfo.Fingers) -} - -func (info *GestureInfo) doAction() error { - for _, action := range actions { - if info.ActionName == action.Name { - if action.fn != nil { - return action.fn() - } - } - } - return nil -} diff --git a/gesture1/manager.go b/gesture1/manager.go index 888b6d12c..93323ea2a 100644 --- a/gesture1/manager.go +++ b/gesture1/manager.go @@ -5,28 +5,24 @@ package gesture1 import ( - "encoding/json" "fmt" - - "github.com/linuxdeepin/dde-daemon/common/dconfig" - "math" "os" "strings" "sync" + "sync/atomic" + "time" + + "github.com/linuxdeepin/dde-daemon/common/dconfig" "github.com/godbus/dbus/v5" wm "github.com/linuxdeepin/go-dbus-factory/session/com.deepin.wm" - clipboard "github.com/linuxdeepin/go-dbus-factory/session/org.deepin.dde.clipboard1" dock "github.com/linuxdeepin/go-dbus-factory/session/org.deepin.dde.daemon.dock1" display "github.com/linuxdeepin/go-dbus-factory/session/org.deepin.dde.display1" - inputdevices "github.com/linuxdeepin/go-dbus-factory/session/org.deepin.dde.inputdevices1" - launchpad "github.com/linuxdeepin/go-dbus-factory/session/org.deepin.dde.launcher1" - notification "github.com/linuxdeepin/go-dbus-factory/session/org.deepin.dde.notification1" sessionmanager "github.com/linuxdeepin/go-dbus-factory/session/org.deepin.dde.sessionmanager1" - sessionwatcher "github.com/linuxdeepin/go-dbus-factory/session/org.deepin.dde.sessionwatcher1" daemon "github.com/linuxdeepin/go-dbus-factory/system/org.deepin.dde.daemon1" gesture "github.com/linuxdeepin/go-dbus-factory/system/org.deepin.dde.gesture1" + ofdbus "github.com/linuxdeepin/go-dbus-factory/system/org.freedesktop.dbus" "github.com/linuxdeepin/go-lib/dbusutil" "github.com/linuxdeepin/go-lib/dbusutil/proxy" ) @@ -43,28 +39,21 @@ const ( dconfigKeyEdgemovestopDuration = "edgemovestopDuration" dconfigKeyLongpressBlacklist = "longpressBlacklist" - dconfigKeyEnable = "enabled" - dconfigKeyTouchPadEnabled = "touchPadEnabled" dconfigKeyTouchScreenEnabled = "touchScreenEnabled" dconfigKeyLongPressEnable = "longPressEnable" dconfigKeyOneFingerBottomEnable = "oneFingerBottomEnable" - dconfigKeyOneFingerLeftEnable = "oneFingerLeftEnable" dconfigKeyOneFingerRightEnable = "oneFingerRightEnable" ) const ( - availableGesturesWith3Fingers = "availableGesturesWith3Fingers" - availableGesturesWith4Fingers = "availableGesturesWith4Fingers" - availableGesturesWithActionTap = "availableGesturesWithActionTap" + touchRightButtonReleaseAttempts = 3 + touchRightButtonReleaseInterval = 50 * time.Millisecond ) type deviceType int32 // 设备类型(触摸屏,触摸板) -const ( - deviceTouchPad deviceType = iota - deviceTouchScreen -) +const deviceTouchScreen deviceType = iota var _useWayland bool @@ -74,37 +63,32 @@ func setUseWayland(value bool) { type Manager struct { wm wm.Wm - service *dbusutil.Service sysDaemon daemon.Daemon + systemDBus ofdbus.DBus systemSigLoop *dbusutil.SignalLoop sessionSigLoop *dbusutil.SignalLoop mu sync.RWMutex gesture gesture.Gesture dock dock.Dock display display.Display - touchPadEnabled dconfig.Bool touchScreenEnabled dconfig.Bool - Infos GestureInfos sessionmanager sessionmanager.SessionManager - clipboard clipboard.Clipboard - notification notification.Notification longPressEnable dconfig.Bool oneFingerBottomEnable dconfig.Bool - oneFingerLeftEnable dconfig.Bool oneFingerRightEnable dconfig.Bool configManagerPath dbus.ObjectPath - sessionWatcher sessionwatcher.SessionWatcher - launchpad launchpad.Launcher dconfigGesture *dconfig.DConfig dconfigTouchScreen *dconfig.DConfig - availableGestures map[string][]string - gestureDisabledTemp bool + touchRightButtonMu sync.Mutex + touchRightButtonDown bool + touchRightButtonStop bool + destroyed atomic.Bool } -func newManager(service *dbusutil.Service) (*Manager, error) { +func newManager() (*Manager, error) { setUseWayland(len(os.Getenv("WAYLAND_DISPLAY")) != 0) sessionConn, err := dbus.SessionBus() if err != nil { @@ -133,39 +117,19 @@ func newManager(service *dbusutil.Service) (*Manager, error) { dock: dock.NewDock(sessionConn), display: display.NewDisplay(sessionConn), sysDaemon: daemon.NewDaemon(systemConn), + systemDBus: ofdbus.NewDBus(systemConn), sessionmanager: sessionmanager.NewSessionManager(sessionConn), - clipboard: clipboard.NewClipboard(sessionConn), - notification: notification.NewNotification(sessionConn), - launchpad: launchpad.NewLauncher(sessionConn), - availableGestures: make(map[string][]string), - service: service, } - m.touchPadEnabled.Bind(m.dconfigGesture, dconfigKeyTouchPadEnabled) m.touchScreenEnabled.Bind(m.dconfigGesture, dconfigKeyTouchScreenEnabled) m.longPressEnable.Bind(m.dconfigGesture, dconfigKeyLongPressEnable) m.oneFingerBottomEnable.Bind(m.dconfigGesture, dconfigKeyOneFingerBottomEnable) - m.oneFingerLeftEnable.Bind(m.dconfigGesture, dconfigKeyOneFingerLeftEnable) m.oneFingerRightEnable.Bind(m.dconfigGesture, dconfigKeyOneFingerRightEnable) - m.availableGestures[availableGesturesWith3Fingers] = m.getAvailableGestureConfigValue(availableGesturesWith3Fingers) - m.availableGestures[availableGesturesWith4Fingers] = m.getAvailableGestureConfigValue(availableGesturesWith4Fingers) - m.availableGestures[availableGesturesWithActionTap] = m.getAvailableGestureConfigValue(availableGesturesWithActionTap) - m.Infos = m.getGestureConfig() - - if _useWayland { - setLongPressEnable(m.longPressEnable.Get()) - } - m.gesture = gesture.NewGesture(systemConn) m.systemSigLoop = dbusutil.NewSignalLoop(systemConn, 10) m.sessionSigLoop = dbusutil.NewSignalLoop(sessionConn, 10) - if _useWayland { - m.sessionWatcher = sessionwatcher.NewSessionWatcher(sessionConn) - } - m.saveGestureConfig() - return m, nil } @@ -182,144 +146,163 @@ func setLongPressEnable(enable bool) { } } -func (m *Manager) getAvailableGestureConfigValue(key string) []string { - data, err := m.dconfigGesture.GetValueStringList(key) +func (m *Manager) destroy() { + if !m.destroyed.CompareAndSwap(false, true) { + return + } + if err := m.stopTouchRightButton(); err != nil { + logger.Warning("release touch right button failed:", err) + } + m.systemDBus.RemoveHandler(proxy.RemoveAllHandlers) + m.gesture.RemoveHandler(proxy.RemoveAllHandlers) + m.sessionmanager.RemoveHandler(proxy.RemoveAllHandlers) + m.systemSigLoop.Stop() + m.sessionSigLoop.Stop() +} + +func (m *Manager) syncLongPressDuration() { + if m.destroyed.Load() { + return + } + longPress, err := m.dconfigTouchScreen.GetValueInt64(dconfigKeyLongpressDuration) if err != nil { logger.Warning(err) - return nil + return + } + err = m.sysDaemon.SetLongPressDuration(0, uint32(longPress)) + if err != nil { + logger.Warning("call SetLongPressDuration failed:", err) } - - return data } -func (m *Manager) getGestureConfig() (infos GestureInfos) { - data, err := m.dconfigGesture.GetValueString("gestures") +func (m *Manager) syncShortPressDuration() { + if m.destroyed.Load() { + return + } + shortPress, err := m.dconfigTouchScreen.GetValueInt64(dconfigKeyShortpressDuration) if err != nil { logger.Warning(err) - return nil + return } - if len(data) == 0 { - return gestureInfos - } else { - err = json.Unmarshal([]byte(data), &infos) - if err != nil { - logger.Warning("gesture config unmarshal fail:", err.Error()) - return - } + err = m.gesture.SetShortPressDuration(0, uint32(shortPress)) + if err != nil { + logger.Warning("call SetShortPressDuration failed:", err) } - return } -func (m *Manager) saveGestureConfig() { - data, err := json.Marshal(m.Infos) +func (m *Manager) syncEdgeMoveStopDuration() { + if m.destroyed.Load() { + return + } + edgemovestopDuration, err := m.dconfigTouchScreen.GetValueInt64(dconfigKeyEdgemovestopDuration) if err != nil { logger.Warning(err) return } - err = m.dconfigGesture.SetValue("gestures", data) + err = m.gesture.SetEdgeMoveStopDuration(0, uint32(edgemovestopDuration)) if err != nil { - logger.Warning(err) + logger.Warning("call SetEdgeMoveStopDuration failed:", err) } - return } -func (m *Manager) destroy() { - m.gesture.RemoveHandler(proxy.RemoveAllHandlers) - m.systemSigLoop.Stop() - m.sessionSigLoop.Stop() +func (m *Manager) syncTouchscreenParameters() { + m.syncLongPressDuration() + m.syncShortPressDuration() + m.syncEdgeMoveStopDuration() } -// initMouseExistWatcher 监听 inputdevices1 的 Mouse Exist 和 DisableTpad 属性变更 -// 当鼠标插入/拔出或用户切换"插入鼠标时禁用触控板"时,更新手势临时禁用状态 -func (m *Manager) initMouseExistWatcher() { - sessionConn := m.sessionSigLoop.Conn() - mouse := inputdevices.NewMouse(sessionConn) - mouse.InitSignalExt(m.sessionSigLoop, true) +func (m *Manager) initTouchscreenParameters() { + m.dconfigTouchScreen.ConnectConfigChanged(dconfigKeyLongpressDuration, func(interface{}) { + m.syncLongPressDuration() + }) + m.dconfigTouchScreen.ConnectConfigChanged(dconfigKeyShortpressDuration, func(interface{}) { + m.syncShortPressDuration() + }) + m.dconfigTouchScreen.ConnectConfigChanged(dconfigKeyEdgemovestopDuration, func(interface{}) { + m.syncEdgeMoveStopDuration() + }) - err := mouse.Exist().ConnectChanged(func(hasValue bool, exist bool) { - if !hasValue { - logger.Debug("Mouse Exist property invalidated, enable gestures") - m.setGestureDisabledTemp(false) + m.touchScreenEnabled.SetNotifyChangedFunc(func(value interface{}) { + if m.destroyed.Load() { return } - m.syncGestureDisabledTemp(mouse) + enable, ok := value.(bool) + if !ok { + logger.Warningf("invalid touchScreenEnabled value type %T", value) + return + } + if !enable { + m.releaseTouchRightButtonWithLog("touch screen disabled") + } }) - if err != nil { - logger.Warningf("initMouseExistWatcher failed to watch Exist: %v", err) - return - } - err = mouse.DisableTpad().ConnectChanged(func(hasValue bool, disableTpad bool) { - if !hasValue { + if _useWayland { + setLongPressEnable(m.longPressEnable.Get()) + } + m.longPressEnable.SetNotifyChangedFunc(func(value interface{}) { + if m.destroyed.Load() { return } - if !disableTpad { - m.setGestureDisabledTemp(false) + enable, ok := value.(bool) + if !ok { + logger.Warningf("invalid longPressEnable value type %T", value) return } - // DisableTpad 开启时,还需检查鼠标是否实际存在 - m.syncGestureDisabledTemp(mouse) + if !enable { + m.releaseTouchRightButtonWithLog("long press disabled") + } + if _useWayland { + setLongPressEnable(enable) + } + }) +} + +func (m *Manager) initSessionWatcher() { + m.sessionmanager.InitSignalExt(m.sessionSigLoop, true) + err := m.sessionmanager.Locked().ConnectChanged(func(hasValue, locked bool) { + if m.destroyed.Load() || !hasValue || !locked { + return + } + m.releaseTouchRightButtonWithLog("session locked") }) if err != nil { - logger.Warningf("initMouseExistWatcher failed to watch DisableTpad: %v", err) - return + logger.Warning("connect session locked property changed failed:", err) } - - m.syncGestureDisabledTemp(mouse) + m.sessionSigLoop.Start() } -func (m *Manager) setGestureDisabledTemp(disable bool) { - m.mu.Lock() - m.gestureDisabledTemp = disable - m.mu.Unlock() +func (m *Manager) releaseTouchRightButtonWithLog(reason string) { + if err := m.releaseTouchRightButton(); err != nil { + logger.Warningf("release touch right button after %s failed: %v", reason, err) + } } -func (m *Manager) syncGestureDisabledTemp(mouse inputdevices.Mouse) { - exist, err := mouse.Exist().Get(0) +func (m *Manager) initSystemGestureOwnerWatcher() { + m.systemDBus.InitSignalExt(m.systemSigLoop, true) + _, err := m.systemDBus.ConnectNameOwnerChanged(m.handleSystemGestureOwnerChanged) if err != nil { - logger.Warningf("syncGestureDisabledTemp failed to get Exist: %v", err) - return - } - if !exist { - m.setGestureDisabledTemp(false) - return + logger.Warning("connect system gesture service owner changed failed:", err) } +} - disable, err := mouse.DisableTpad().Get(0) - if err != nil { - logger.Warningf("syncGestureDisabledTemp failed to get DisableTpad: %v", err) +func (m *Manager) handleSystemGestureOwnerChanged(name, oldOwner, newOwner string) { + if name != dbusServiceName || m.destroyed.Load() { return } - m.setGestureDisabledTemp(disable) + if oldOwner != "" || newOwner != "" { + if err := m.releaseTouchRightButton(); err != nil { + logger.Warning("release touch right button after system gesture service owner changed failed:", err) + } + } + if newOwner != "" { + m.syncTouchscreenParameters() + } } func (m *Manager) init() { - m.initActions() - longPress, err := m.dconfigTouchScreen.GetValueInt64(dconfigKeyLongpressDuration) - if err != nil { - logger.Warning(err) - } - err = m.sysDaemon.SetLongPressDuration(0, uint32(longPress)) - if err != nil { - logger.Warning("call SetLongPressDuration failed:", err) - } - shortPress, err := m.dconfigTouchScreen.GetValueInt64(dconfigKeyShortpressDuration) - if err != nil { - logger.Warning(err) - } - err = m.gesture.SetShortPressDuration(0, uint32(shortPress)) - if err != nil { - logger.Warning("call SetShortPressDuration failed:", err) - } - edgemovestopDuration, err := m.dconfigTouchScreen.GetValueInt64(dconfigKeyEdgemovestopDuration) - if err != nil { - logger.Warning(err) - } - err = m.gesture.SetEdgeMoveStopDuration(0, uint32(edgemovestopDuration)) - if err != nil { - logger.Warning("call SetEdgeMoveStopDuration failed:", err) - } + m.initTouchscreenParameters() + m.initSessionWatcher() systemConn, err := dbus.SystemBus() if err != nil { @@ -333,28 +316,19 @@ func (m *Manager) init() { logger.Warning(err) } - m.initMouseExistWatcher() - - m.sessionSigLoop.Start() m.systemSigLoop.Start() + m.initSystemGestureOwnerWatcher() m.gesture.InitSignalExt(m.systemSigLoop, true) + m.syncTouchscreenParameters() _, err = m.gesture.ConnectEvent(func(name string, direction string, fingers int32) { - should, err := m.shouldHandleEvent(deviceTouchPad) - if err != nil { - logger.Error("shouldHandleEvent failed:", err) + if m.destroyed.Load() { return } - if !should { + if !isTouchRightButtonEvent(name, direction) { return } - - err = m.Exec(EventInfo{ - Name: name, - Direction: direction, - Fingers: fingers, - }) - if err != nil { - logger.Error("Exec failed:", err) + if err := m.handleTouchRightButton(direction); err != nil { + logger.Error("handle touch right button failed:", err) } }) @@ -442,14 +416,86 @@ func (m *Manager) init() { } } -func (m *Manager) shouldIgnoreGesture(info *GestureInfo) bool { +func isTouchRightButtonEvent(name, direction string) bool { + if name != "touch right button" { + return false + } + return direction == "down" || direction == "up" +} + +func (m *Manager) handleTouchRightButton(direction string) error { + if direction == "up" { + return m.releaseTouchRightButton() + } + + should, err := m.shouldHandleEvent(deviceTouchScreen) + if err != nil { + return fmt.Errorf("shouldHandleEvent failed: %w", err) + } + if !should || !m.longPressEnable.Get() || _useWayland { + return nil + } + + if m.shouldIgnoreGesture("touch right button", direction) { + return nil + } + + if direction != "down" { + return nil + } + + m.touchRightButtonMu.Lock() + defer m.touchRightButtonMu.Unlock() + if m.touchRightButtonStop || m.touchRightButtonDown { + return nil + } + if err := m.doXdotoolsMouseDown(); err != nil { + return err + } + m.touchRightButtonDown = true + return nil +} + +func (m *Manager) releaseTouchRightButton() error { + m.touchRightButtonMu.Lock() + defer m.touchRightButtonMu.Unlock() + return m.releaseTouchRightButtonLocked() +} + +func (m *Manager) stopTouchRightButton() error { + m.touchRightButtonMu.Lock() + defer m.touchRightButtonMu.Unlock() + m.touchRightButtonStop = true + return m.releaseTouchRightButtonLocked() +} + +func (m *Manager) releaseTouchRightButtonLocked() error { + if !m.touchRightButtonDown { + return nil + } + var lastErr error + for attempt := 0; attempt < touchRightButtonReleaseAttempts; attempt++ { + if err := m.doXdotoolsMouseUp(); err == nil { + m.touchRightButtonDown = false + return nil + } else { + lastErr = err + } + if attempt+1 < touchRightButtonReleaseAttempts { + time.Sleep(touchRightButtonReleaseInterval) + } + } + return fmt.Errorf("mouseup failed after %d attempts: %w", touchRightButtonReleaseAttempts, lastErr) +} + +func (m *Manager) shouldIgnoreGesture(name, direction string) bool { // allow right button up when kbd grabbed - if (info.Name != "touch right button" || info.Direction != "up") && isKbdAlreadyGrabbed() { + if (name != "touch right button" || direction != "up") && isKbdAlreadyGrabbed() { // 多任务窗口下,不应该忽略手势操作 isShowMultiTask, err := m.wm.GetMultiTaskingStatus(0) if err != nil { logger.Warning(err) - } else if isShowMultiTask && info.Name == "swipe" { + } else if isShowMultiTask && name == "swipe" { logger.Debug("should not ignore swipe event, because we are in multi task") return false } @@ -458,7 +504,7 @@ func (m *Manager) shouldIgnoreGesture(info *GestureInfo) bool { } // TODO(jouyouyun): improve touch right button handler - if info.Name == "touch right button" { + if name == "touch right button" { // filter google chrome blackList, err := m.dconfigTouchScreen.GetValueStringList(dconfigKeyLongpressBlacklist) if err != nil { @@ -468,46 +514,13 @@ func (m *Manager) shouldIgnoreGesture(info *GestureInfo) bool { logger.Debug("the current active window in blacklist") return true } - } else if strings.HasPrefix(info.Name, "touch") { + } else if strings.HasPrefix(name, "touch") { return true } return false } -func (m *Manager) Exec(evInfo EventInfo) error { - if _useWayland { - if !isSessionActive("/org/freedesktop/login1/session/self") { - active, err := m.sessionWatcher.IsActive().Get(0) - if err != nil || !active { - logger.Debug("Gesture had been disabled or session inactive") - return nil - } - } - } - - info := m.GetGestureByEvent(evInfo) - if info == nil { - logger.Infof("[Exec]: not found event info: %s", evInfo.toString()) - return nil - } - - logger.Debugf("[Exec]: event info:%s", info.toString()) - if m.shouldIgnoreGesture(info) { - return nil - } - - if (!m.longPressEnable.Get() || _useWayland) && strings.Contains(string(info.Name), "touch right button") { - return nil - } - - return info.doAction() -} - -func (*Manager) GetInterfaceName() string { - return dbusServiceIFC -} - type TouchScreensRotation uint16 // counterclockwise @@ -724,10 +737,6 @@ func (m *Manager) handleTouchEdgeEvent(context *touchEventContext, edge string, logger.Debugf("handleTouchEdgeEvent: context:%+v edge:%s p:%+v", *context, edge, *p) switch edge { case context.left: - // 禁用触摸屏左侧划入唤出剪贴板功能 - // if p.X*float64(context.screenHeight) > 100 && m.oneFingerLeftEnable.Get() { - // return m.clipboard.Show(0) - // } case context.right: if (1-p.X)*float64(context.screenWidth) > 100 && m.oneFingerRightEnable.Get() { return m.showWidgets(true) @@ -755,10 +764,6 @@ func (m *Manager) handleTouchMovementEvent(context *touchEventContext, direction switch direction { case context.left: - // 禁用触摸屏左侧划入隐藏剪贴板功能 - // if m.oneFingerLeftEnable.Get() { - // return m.clipboard.Hide(0) - // } case context.right: if m.oneFingerRightEnable.Get() { return m.showWidgets(false) @@ -799,15 +804,6 @@ func (m *Manager) shouldHandleEvent(devType deviceType) (bool, error) { defer m.mu.RUnlock() switch devType { - case deviceTouchPad: - if m.gestureDisabledTemp { - logger.Debug("touch pad is temporarily disabled by mouse, do not handle touchpad gesture event") - return false, nil - } - if !m.touchPadEnabled.Get() { - logger.Debug("touch pad is disabled, do not handle touchpad gesture event") - return false, nil - } case deviceTouchScreen: if !m.touchScreenEnabled.Get() { logger.Debug("touch screen is disabled, do not handle touchscreen gesture event") diff --git a/gesture1/manager_ifc.go b/gesture1/manager_ifc.go deleted file mode 100644 index c0be9c84e..000000000 --- a/gesture1/manager_ifc.go +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. -// -// SPDX-License-Identifier: GPL-3.0-or-later - -package gesture1 - -import ( - "encoding/json" - "fmt" - - "github.com/godbus/dbus/v5" - "github.com/linuxdeepin/go-lib/dbusutil" - "github.com/linuxdeepin/go-lib/strv" -) - -func (m *Manager) SetLongPressDuration(duration uint32) *dbus.Error { - if value, _ := m.dconfigTouchScreen.GetValueInt64(dconfigKeyLongpressDuration); value == int64(duration) { - return nil - } - err := m.sysDaemon.SetLongPressDuration(0, duration) - if err != nil { - return dbusutil.ToError(err) - } - m.dconfigTouchScreen.SetValue(dconfigKeyLongpressDuration, int64(duration)) - return nil -} - -func (m *Manager) GetLongPressDuration() (duration uint32, busErr *dbus.Error) { - value, err := m.dconfigTouchScreen.GetValueInt64(dconfigKeyLongpressDuration) - return uint32(value), dbusutil.ToError(err) -} - -func (m *Manager) SetShortPressDuration(duration uint32) *dbus.Error { - if value, _ := m.dconfigTouchScreen.GetValueInt64(dconfigKeyShortpressDuration); value == int64(duration) { - return nil - } - err := m.gesture.SetShortPressDuration(0, duration) - if err != nil { - return dbusutil.ToError(err) - } - m.dconfigTouchScreen.SetValue(dconfigKeyShortpressDuration, duration) - return nil -} - -func (m *Manager) GetShortPressDuration() (duration uint32, busErr *dbus.Error) { - value, err := m.dconfigTouchScreen.GetValueInt64(dconfigKeyShortpressDuration) - return uint32(value), dbusutil.ToError(err) -} - -func (m *Manager) SetEdgeMoveStopDuration(duration uint32) *dbus.Error { - if value, _ := m.dconfigTouchScreen.GetValueInt64(dconfigKeyEdgemovestopDuration); value == int64(duration) { - return nil - } - err := m.gesture.SetEdgeMoveStopDuration(0, duration) - if err != nil { - return dbusutil.ToError(err) - } - m.dconfigTouchScreen.SetValue(dconfigKeyEdgemovestopDuration, int32(duration)) - return nil -} - -func (m *Manager) GetEdgeMoveStopDuration() (duration uint32, busErr *dbus.Error) { - value, err := m.dconfigTouchScreen.GetValueInt64(dconfigKeyEdgemovestopDuration) - return uint32(value), dbusutil.ToError(err) -} - -// GetGestureAvaiableActions 获取手势可选的操作 -func (m *Manager) GetGestureAvaiableActions(name string, fingers int32) (string, *dbus.Error) { - search := func(typ string) (string, *dbus.Error) { - if m.availableGestures[typ] == nil { - return "", dbusutil.ToError(fmt.Errorf("%s has no available actions", typ)) - } - var actionMap actionInfos - for _, gusture := range m.availableGestures[typ] { - for _, info := range actions { - if info.Name == gusture { - actionMap = append(actionMap, info) - continue - } - } - } - infos, err := json.Marshal(actionMap) - if err != nil { - return "", dbusutil.ToError(err) - } - return string(infos), nil - } - if name == "swipe" { - if fingers == 3 { - return search(availableGesturesWith3Fingers) - } else if fingers == 4 { - return search(availableGesturesWith4Fingers) - } - } else if name == "tap" { - return search(availableGesturesWithActionTap) - } - return "", nil -} - -func (m *Manager) SetGesture(name string, direction string, fingers int32, action string) *dbus.Error { - var typ string - if name == "swipe" { - if fingers == 3 { - typ = availableGesturesWith3Fingers - - } else if fingers == 4 { - typ = availableGesturesWith4Fingers - } - } else if name == "tap" { - typ = availableGesturesWithActionTap - } - if typ != "" { - available := m.availableGestures[typ] - if !strv.Strv(available).Contains(action) { - return dbusutil.ToError(fmt.Errorf("actions %v not found", action)) - } - } - for _, gesture := range m.Infos { - if gesture.Name == name && - gesture.Direction == direction && - gesture.Fingers == fingers { - gesture.ActionName = action - err := m.emitPropChangedInfos(m.Infos) - if err != nil { - logger.Warning(err) - } - m.saveGestureConfig() - break - } - } - return nil -} diff --git a/gesture1/utils.go b/gesture1/utils.go index 5850a4711..17a5a1393 100644 --- a/gesture1/utils.go +++ b/gesture1/utils.go @@ -62,12 +62,17 @@ func isKbdAlreadyGrabbed() bool { } func getCurrentActionWindowCmd() string { - win, err := ewmh.GetActiveWindow(xconn).Reply(xconn) + conn := getX11Conn() + if conn == nil { + return "" + } + + win, err := ewmh.GetActiveWindow(conn).Reply(conn) if err != nil { logger.Warning("Failed to get current active window:", err) return "" } - pid, err := ewmh.GetWMPid(xconn, win).Reply(xconn) + pid, err := ewmh.GetWMPid(conn, win).Reply(conn) if err != nil { logger.Warning("Failed to get current window pid:", err) return "" diff --git a/misc/dsg-configs/org.deepin.dde.daemon.loader.json b/misc/dsg-configs/org.deepin.dde.daemon.loader.json index 8444e1dde..eb19fb551 100644 --- a/misc/dsg-configs/org.deepin.dde.daemon.loader.json +++ b/misc/dsg-configs/org.deepin.dde.daemon.loader.json @@ -133,7 +133,7 @@ "visibility": "private" }, "keybinding": { - "value": true, + "value": false, "serial": 0, "flags": [], "name": "keybindingEnable", @@ -324,5 +324,3 @@ } } } - - \ No newline at end of file diff --git a/misc/services/org.deepin.dde.Keybinding1.service b/misc/services/org.deepin.dde.Keybinding1.service deleted file mode 100644 index 450a59052..000000000 --- a/misc/services/org.deepin.dde.Keybinding1.service +++ /dev/null @@ -1,4 +0,0 @@ -[D-BUS Service] -Name=org.deepin.dde.Keybinding1 -Exec=/usr/lib/deepin-daemon/dde-session-daemon -SystemdService=org.dde.session.Daemon1.service diff --git a/system/gesture1/core.c b/system/gesture1/core.c index d58ff38b3..e55890274 100644 --- a/system/gesture1/core.c +++ b/system/gesture1/core.c @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include @@ -28,11 +30,22 @@ struct raw_multitouch_event { int fingers; uint64_t t_start_tap; guint tap_id; + guint tap_generation; + char *device_node; bool dblclick; bool ignore; }; +struct tap_timer_data { + char *device_node; + guint generation; + int fingers; +}; + static void raw_event_reset(struct raw_multitouch_event *event, bool reset_dblclick); +static struct raw_multitouch_event *raw_event_new(const char *device_node); +static void raw_event_free(gpointer data); +static void handle_tap_stop(struct raw_multitouch_event *event); static int is_touchpad(struct libinput_device *dev); static const char* get_multitouch_device_node(struct libinput_event *ev); @@ -44,11 +57,28 @@ static gboolean touch_timer_handler(gpointer data); static void touch_timer_destroy(gpointer data); static void start_touch_timer(); static void stop_touch_timer(); +static void release_touch_right_button(void); +static int touch_right_button_sent(void); +static void stop_all_active_swipes(void); static int valid_long_press_touch(double x, double y); static GHashTable *ev_table = NULL; struct raw_multitouch_event *raw = NULL; -static int quit = 0; +static GMutex tap_mutex; +static GMutex right_button_mutex; + +static GMutex timer_callback_mutex; +static GCond timer_callback_cond; +static guint active_timer_callbacks = 0; +static bool timer_callbacks_stopping = true; + +static GMutex loop_mutex; +static GCond loop_start_cond; +static int quit_requested = 0; +static int quit_write_fd = -1; +static bool loop_active = false; +static bool loop_start_reported = false; +static int loop_start_result = 0; static struct _long_press_timer { guint id; @@ -75,11 +105,173 @@ static uint64_t _prev_ev_time; // 前一个非 TOUCH_FRAME 事件的时间,单 static int _prev_ev_type; // 前一个非 TOUCH_FRAME 事件的类型 static uint64_t _prev_frame_ev_time; // 前一个 TOUCH_FRAME 事件的时间,单位 usec +static bool +timer_callback_enter(void) +{ + bool allowed = false; + + g_mutex_lock(&timer_callback_mutex); + if (!timer_callbacks_stopping) { + active_timer_callbacks++; + allowed = true; + } + g_mutex_unlock(&timer_callback_mutex); + return allowed; +} + +static void +timer_callback_leave(void) +{ + g_mutex_lock(&timer_callback_mutex); + active_timer_callbacks--; + if (timer_callbacks_stopping && active_timer_callbacks == 0) { + g_cond_signal(&timer_callback_cond); + } + g_mutex_unlock(&timer_callback_mutex); +} + +static void +start_timer_callbacks(void) +{ + g_mutex_lock(&timer_callback_mutex); + timer_callbacks_stopping = false; + g_mutex_unlock(&timer_callback_mutex); +} + +static void +stop_timer_callbacks(void) +{ + g_mutex_lock(&timer_callback_mutex); + timer_callbacks_stopping = true; + while (active_timer_callbacks != 0) { + g_cond_wait(&timer_callback_cond, &timer_callback_mutex); + } + g_mutex_unlock(&timer_callback_mutex); +} + +static struct raw_multitouch_event * +raw_event_new(const char *device_node) +{ + struct raw_multitouch_event *event = g_new0(struct raw_multitouch_event, 1); + event->device_node = g_strdup(device_node); + return event; +} + +static void +raw_event_free(gpointer data) +{ + struct raw_multitouch_event *event = data; + if (!event) { + return; + } + g_free(event->device_node); + g_free(event); +} + +static void +cancel_all_tap_timers(void) +{ + GArray *tap_ids = g_array_new(FALSE, FALSE, sizeof(guint)); + + g_mutex_lock(&tap_mutex); + if (ev_table) { + GHashTableIter iter; + gpointer value; + g_hash_table_iter_init(&iter, ev_table); + while (g_hash_table_iter_next(&iter, NULL, &value)) { + struct raw_multitouch_event *event = value; + if (event->tap_id) { + g_array_append_val(tap_ids, event->tap_id); + event->tap_id = 0; + event->tap_generation++; + } + } + } + g_mutex_unlock(&tap_mutex); + + for (guint i = 0; i < tap_ids->len; i++) { + g_source_remove(g_array_index(tap_ids, guint, i)); + } + g_array_free(tap_ids, TRUE); +} + +static void +reset_loop_state(void) +{ + g_mutex_lock(&loop_mutex); + quit_write_fd = -1; + quit_requested = 0; + loop_active = false; + g_mutex_unlock(&loop_mutex); +} + +static void +report_loop_start(int result) +{ + g_mutex_lock(&loop_mutex); + loop_start_result = result; + loop_start_reported = true; + g_cond_broadcast(&loop_start_cond); + g_mutex_unlock(&loop_mutex); +} + +void +prepare_loop(void) +{ + g_mutex_lock(&loop_mutex); + quit_requested = 0; + loop_active = true; + loop_start_reported = false; + loop_start_result = 0; + g_mutex_unlock(&loop_mutex); +} + +int +wait_loop_ready(void) +{ + g_mutex_lock(&loop_mutex); + while (!loop_start_reported) { + g_cond_wait(&loop_start_cond, &loop_mutex); + } + int result = loop_start_result; + g_mutex_unlock(&loop_mutex); + return result; +} + int start_loop(int verbose, double distance) { + int wake_pipe[2] = {-1, -1}; + if (pipe(wake_pipe) < 0) { + fprintf(stderr, "Failed to create gesture wake pipe: %s\n", strerror(errno)); + report_loop_start(0); + reset_loop_state(); + return -1; + } + fcntl(wake_pipe[0], F_SETFL, fcntl(wake_pipe[0], F_GETFL) | O_NONBLOCK); + fcntl(wake_pipe[1], F_SETFL, fcntl(wake_pipe[1], F_GETFL) | O_NONBLOCK); + fcntl(wake_pipe[0], F_SETFD, FD_CLOEXEC); + fcntl(wake_pipe[1], F_SETFD, FD_CLOEXEC); + + g_mutex_lock(&loop_mutex); + if (quit_requested) { + quit_requested = 0; + loop_active = false; + g_mutex_unlock(&loop_mutex); + close(wake_pipe[0]); + close(wake_pipe[1]); + report_loop_start(0); + return 0; + } + quit_write_fd = wake_pipe[1]; + g_mutex_unlock(&loop_mutex); + struct libinput *li = open_from_udev("seat0", NULL, verbose); if (!li) { + report_loop_start(0); + reset_loop_state(); + close(wake_pipe[0]); + close(wake_pipe[1]); return -1; } @@ -92,12 +284,18 @@ start_loop(int verbose, double distance) ev_table = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, - (GDestroyNotify)g_free); + raw_event_free); if (!ev_table) { fprintf(stderr, "Failed to initialize event table\n"); libinput_unref(li); + report_loop_start(0); + reset_loop_state(); + close(wake_pipe[0]); + close(wake_pipe[1]); return -1; } + start_timer_callbacks(); + report_loop_start(1); // movements have pointer structs inside struct movement movements[MOV_SLOTS] = {{{0}}}; @@ -105,29 +303,70 @@ start_loop(int verbose, double distance) // firstly handle all devices handle_events(li, movements); - struct pollfd fds; - fds.fd = libinput_get_fd(li); - fds.events = POLLIN; - fds.revents = 0; + struct pollfd fds[2] = { + {.fd = libinput_get_fd(li), .events = POLLIN, .revents = 0}, + {.fd = wake_pipe[0], .events = POLLIN, .revents = 0}, + }; + + while (true) { + if (poll(fds, 2, -1) < 0) { + if (errno == EINTR) { + continue; + } + fprintf(stderr, "Gesture event poll failed: %s\n", strerror(errno)); + break; + } - quit = 0; - while(!quit) { - if(poll(&fds, 1, -1) < 0) - { - continue; + if (fds[1].revents & (POLLIN | POLLERR | POLLHUP)) { + char buffer[16]; + while (read(wake_pipe[0], buffer, sizeof(buffer)) > 0) { + } + break; } - handle_events(li, movements); - handle_movements(movements); //handle touch screen + if (fds[0].revents & POLLIN) { + handle_events(li, movements); + handle_movements(movements); //handle touch screen + } } + stop_timer_callbacks(); + stop_touch_timer(); + release_touch_right_button(); + stop_all_active_swipes(); + cancel_all_tap_timers(); + + g_mutex_lock(&tap_mutex); + raw = NULL; + g_hash_table_destroy(ev_table); + ev_table = NULL; + g_mutex_unlock(&tap_mutex); + + libinput_unref(li); + reset_loop_state(); + close(wake_pipe[0]); + close(wake_pipe[1]); return 0; } void quit_loop(void) { - quit = 1; + int wake_fd; + + g_mutex_lock(&loop_mutex); + if (!loop_active) { + g_mutex_unlock(&loop_mutex); + return; + } + quit_requested = 1; + wake_fd = quit_write_fd; + if (wake_fd >= 0) { + const char wake = 1; + ssize_t unused = write(wake_fd, &wake, sizeof(wake)); + (void)unused; + } + g_mutex_unlock(&loop_mutex); } void @@ -171,7 +410,7 @@ raw_event_reset(struct raw_multitouch_event *event, bool reset_dblclick) event->scale = 0.0; event->fingers = 0; event->t_start_tap = 0; - event->tap_id = 0; + handle_tap_stop(event); if (reset_dblclick) event->dblclick = false; } @@ -204,26 +443,42 @@ is_touchpad(struct libinput_device *dev) static gboolean touch_timer_handler(gpointer data) { + if (!timer_callback_enter()) { + return FALSE; + } g_debug("touch timer arrived: %u, data: (%f. %f)", touch_timer.id, touch_timer.x, touch_timer.y); - handleTouchEvent(TOUCH_TYPE_RIGHT_BUTTON, BUTTON_TYPE_DOWN); - touch_timer.sent = 1; + g_mutex_lock(&right_button_mutex); + if (!touch_timer.sent) { + touch_timer.sent = 1; + handleTouchEvent(TOUCH_TYPE_RIGHT_BUTTON, BUTTON_TYPE_DOWN); + } + g_mutex_unlock(&right_button_mutex); + timer_callback_leave(); return FALSE; } static gboolean short_press_timer_handler(gpointer data) { + if (!timer_callback_enter()) { + return FALSE; + } point scale = get_last_point_scale(); handleTouchShortPress(short_press_duration, scale.x, scale.y); + timer_callback_leave(); return FALSE; } static gboolean long_press_timer_handler2(gpointer data) { + if (!timer_callback_enter()) { + return FALSE; + } point scale = get_last_point_scale(); handleTouchPressTimeout(1, long_press_duration2, scale.x, scale.y); + timer_callback_leave(); return FALSE; } @@ -283,6 +538,53 @@ stop_touch_timer() } } +static void +release_touch_right_button(void) +{ + g_mutex_lock(&right_button_mutex); + if (touch_timer.sent) { + touch_timer.sent = 0; + handleTouchEvent(TOUCH_TYPE_RIGHT_BUTTON, BUTTON_TYPE_UP); + } + g_mutex_unlock(&right_button_mutex); +} + +static int +touch_right_button_sent(void) +{ + g_mutex_lock(&right_button_mutex); + int sent = touch_timer.sent; + g_mutex_unlock(&right_button_mutex); + return sent; +} + +static void +stop_all_active_swipes(void) +{ + GArray *fingers = g_array_new(FALSE, FALSE, sizeof(int)); + + g_mutex_lock(&tap_mutex); + if (ev_table) { + GHashTableIter iter; + gpointer value; + g_hash_table_iter_init(&iter, ev_table); + while (g_hash_table_iter_next(&iter, NULL, &value)) { + struct raw_multitouch_event *event = value; + if (event->dblclick) { + int finger_count = event->fingers; + event->dblclick = false; + g_array_append_val(fingers, finger_count); + } + } + } + g_mutex_unlock(&tap_mutex); + + for (guint i = 0; i < fingers->len; i++) { + handleSwipeStop(g_array_index(fingers, int, i)); + } + g_array_free(fingers, TRUE); +} + static int valid_long_press_touch(double x, double y) { @@ -296,35 +598,88 @@ valid_long_press_touch(double x, double y) } static gboolean - handle_tap(gpointer data) +handle_tap(gpointer data) { - g_debug("[Tap] fingers: %d", raw->fingers); - handleGestureEvent(GESTURE_TYPE_TAP, GESTURE_DIRECTION_NONE, raw->fingers); + struct tap_timer_data *timer_data = data; + if (!timer_data || !timer_callback_enter()) { + return FALSE; + } + + bool valid = false; + g_mutex_lock(&tap_mutex); + if (ev_table) { + struct raw_multitouch_event *event = g_hash_table_lookup(ev_table, timer_data->device_node); + if (event && event->tap_id != 0 && event->tap_generation == timer_data->generation) { + event->tap_id = 0; + valid = true; + } + } + g_mutex_unlock(&tap_mutex); + + if (valid) { + g_debug("[Tap] fingers: %d", timer_data->fingers); + handleGestureEvent(GESTURE_TYPE_TAP, GESTURE_DIRECTION_NONE, timer_data->fingers); + } + timer_callback_leave(); return FALSE; } static void handle_tap_destroy(gpointer data) { - if (raw && raw->tap_id) { - raw->tap_id = 0; + struct tap_timer_data *timer_data = data; + if (!timer_data) { + return; } + g_free(timer_data->device_node); + g_free(timer_data); } static void -handle_tap_stop() +handle_tap_stop(struct raw_multitouch_event *event) { - if (raw && raw->tap_id) { - g_source_remove(raw->tap_id); - raw->tap_id = 0; + guint tap_id = 0; + + g_mutex_lock(&tap_mutex); + if (event && event->tap_id) { + tap_id = event->tap_id; + event->tap_id = 0; + event->tap_generation++; + } + g_mutex_unlock(&tap_mutex); + + if (tap_id) { + g_source_remove(tap_id); } } static int -handle_tap_delay() +handle_tap_delay(struct raw_multitouch_event *event) { - raw->tap_id = g_timeout_add_full(G_PRIORITY_DEFAULT, dblclick_duration, - handle_tap, NULL, handle_tap_destroy); + if (!event || !event->device_node) { + return 0; + } + + struct tap_timer_data *timer_data = g_new0(struct tap_timer_data, 1); + timer_data->device_node = g_strdup(event->device_node); + timer_data->fingers = event->fingers; + + g_mutex_lock(&tap_mutex); + if (event->tap_id) { + g_mutex_unlock(&tap_mutex); + handle_tap_destroy(timer_data); + return 0; + } + timer_data->generation = ++event->tap_generation; + event->tap_id = g_timeout_add_full(G_PRIORITY_DEFAULT, dblclick_duration, + handle_tap, timer_data, handle_tap_destroy); + guint tap_id = event->tap_id; + g_mutex_unlock(&tap_mutex); + + if (!tap_id) { + handle_tap_destroy(timer_data); + } + return tap_id != 0; } /** @@ -349,7 +704,9 @@ handle_gesture_events(struct libinput_event *ev, int type) } const char *node = udev_device_get_devnode(libinput_device_get_udev_device(dev)); - raw = g_hash_table_lookup(ev_table, node); + g_mutex_lock(&tap_mutex); + raw = ev_table ? g_hash_table_lookup(ev_table, node) : NULL; + g_mutex_unlock(&tap_mutex); if (!raw) { fprintf(stderr, "Not found '%s' in table\n", node); return ; @@ -370,6 +727,7 @@ handle_gesture_events(struct libinput_event *ev, int type) case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: // reset raw_event_reset(raw, false); + raw->fingers = libinput_event_gesture_get_finger_count(gesture); break; case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:{ double scale = libinput_event_gesture_get_scale(gesture); @@ -384,6 +742,7 @@ handle_gesture_events(struct libinput_event *ev, int type) raw->dy_unaccel += dy_unaccel; int fingers = libinput_event_gesture_get_finger_count(gesture); + raw->fingers = fingers; if (raw->dblclick) { handleSwipeMoving(fingers, dx_unaccel, dy_unaccel); } @@ -408,6 +767,10 @@ handle_gesture_events(struct libinput_event *ev, int type) } case LIBINPUT_EVENT_GESTURE_SWIPE_END: if (libinput_event_gesture_get_cancelled(gesture)) { + raw->fingers = libinput_event_gesture_get_finger_count(gesture); + if (raw->dblclick) { + handleSwipeStop(raw->fingers); + } raw_event_reset(raw, true); break; } @@ -447,25 +810,29 @@ handle_gesture_events(struct libinput_event *ev, int type) if (raw->t_start_tap > 0 && (libinput_event_gesture_get_time_usec(gesture) - raw->t_start_tap) / 1000 <= dblclick_duration && raw->fingers == libinput_event_gesture_get_finger_count(gesture)) { + int fingers = raw->fingers; handleDbclickDown(raw->fingers); - handle_tap_stop(); + handle_tap_stop(raw); raw_event_reset(raw, true); + raw->fingers = fingers; raw->dblclick = true; } break; case LIBINPUT_EVENT_GESTURE_HOLD_END: - if (libinput_event_gesture_get_cancelled(gesture)) { + raw->fingers = libinput_event_gesture_get_finger_count(gesture); + if (raw->dblclick) { + handleSwipeStop(raw->fingers); raw_event_reset(raw, true); break; } - if (!raw->dblclick) { - raw->fingers = libinput_event_gesture_get_finger_count(gesture); - raw->t_start_tap = libinput_event_gesture_get_time_usec(gesture); - handle_tap_delay(); - } else { + if (libinput_event_gesture_get_cancelled(gesture)) { raw_event_reset(raw, true); + break; } + + raw->t_start_tap = libinput_event_gesture_get_time_usec(gesture); + handle_tap_delay(raw); break; } } @@ -484,7 +851,9 @@ handle_touch_events(struct libinput_event *ev, int ty,struct movement *m) } node = get_multitouch_device_node(ev); - rme = g_hash_table_lookup(ev_table, node); + g_mutex_lock(&tap_mutex); + rme = ev_table ? g_hash_table_lookup(ev_table, node) : NULL; + g_mutex_unlock(&tap_mutex); if (rme && rme->ignore) { return; } @@ -516,7 +885,7 @@ handle_touch_events(struct libinput_event *ev, int ty,struct movement *m) case LIBINPUT_EVENT_TOUCH_MOTION:{ handle_touch_event_motion(ev, m); g_debug("Touch motion, id: %u, fingers: %d, sent: %d ", - touch_timer.id, touch_timer.fingers, touch_timer.sent); + touch_timer.id, touch_timer.fingers, touch_right_button_sent()); if (touch_timer.id == 0) { break; } @@ -535,15 +904,12 @@ handle_touch_events(struct libinput_event *ev, int ty,struct movement *m) case LIBINPUT_EVENT_TOUCH_UP: handle_touch_event_up(ev, m); g_debug("Touch up, id: %u, fingers: %d, sent: %d ", - touch_timer.id, touch_timer.fingers, touch_timer.sent); + touch_timer.id, touch_timer.fingers, touch_right_button_sent()); stop_touch_timer(); if (touch_timer.fingers > 0) { touch_timer.fingers--; } - if (touch_timer.sent) { - touch_timer.sent = 0; - handleTouchEvent(TOUCH_TYPE_RIGHT_BUTTON, BUTTON_TYPE_UP); - } + release_touch_right_button(); scale = get_last_point_scale(); handleTouchUpOrCancel(scale.x, scale.y); @@ -551,16 +917,18 @@ handle_touch_events(struct libinput_event *ev, int ty,struct movement *m) case LIBINPUT_EVENT_TOUCH_DOWN: { handle_touch_event_down(ev, m); g_debug("Touch down, id: %u, fingers: %d, sent: %d ", - touch_timer.id, touch_timer.fingers, touch_timer.sent); + touch_timer.id, touch_timer.fingers, touch_right_button_sent()); if (touch_timer.id != 0 || touch_timer.fingers > 0) { stop_touch_timer(); touch_timer.fingers++; break; } struct libinput_event_touch *touch = libinput_event_get_touch_event(ev); + g_mutex_lock(&right_button_mutex); + touch_timer.sent = 0; + g_mutex_unlock(&right_button_mutex); start_touch_timer(); touch_timer.fingers = 1; - touch_timer.sent = 0; //w和h需要赋初值,防止libinput_device_get_size函数异常返回后,w和h的随机值>1而导致获取的坐标异常 double w=0.0, h=0.0; int iret = libinput_device_get_size(dev, &w, &h); @@ -581,13 +949,10 @@ handle_touch_events(struct libinput_event *ev, int ty,struct movement *m) case LIBINPUT_EVENT_TOUCH_CANCEL: handle_touch_event_cancel(ev, m); g_debug("Touch cancel, id: %u, fingers: %d, sent: %d ", - touch_timer.id, touch_timer.fingers, touch_timer.sent); + touch_timer.id, touch_timer.fingers, touch_right_button_sent()); stop_touch_timer(); touch_timer.fingers = 0; - if (touch_timer.sent) { - touch_timer.sent = 0; - handleTouchEvent(TOUCH_TYPE_RIGHT_BUTTON, BUTTON_TYPE_UP); - } + release_touch_right_button(); scale = get_last_point_scale(); handleTouchUpOrCancel(scale.x, scale.y); @@ -642,16 +1007,37 @@ handle_events(struct libinput *li, struct movement *m) const char *path = get_multitouch_device_node(ev); if (path) { /* g_debug("Device added: %s", path); */ - g_hash_table_insert(ev_table, g_strdup(path), - g_new0(struct raw_multitouch_event, 1)); + g_mutex_lock(&tap_mutex); + if (ev_table && !g_hash_table_contains(ev_table, path)) { + g_hash_table_insert(ev_table, g_strdup(path), raw_event_new(path)); + } + g_mutex_unlock(&tap_mutex); } break; } case LIBINPUT_EVENT_DEVICE_REMOVED: { const char *path = get_multitouch_device_node(ev); if (path) { + release_touch_right_button(); /* g_debug("Will remove '%s' to table", path); */ - g_hash_table_remove(ev_table, path); + g_mutex_lock(&tap_mutex); + struct raw_multitouch_event *event = ev_table ? g_hash_table_lookup(ev_table, path) : NULL; + g_mutex_unlock(&tap_mutex); + if (event) { + handle_tap_stop(event); + if (event->dblclick) { + handleSwipeStop(event->fingers); + } + raw_event_reset(event, true); + if (raw == event) { + raw = NULL; + } + } + g_mutex_lock(&tap_mutex); + if (ev_table) { + g_hash_table_remove(ev_table, path); + } + g_mutex_unlock(&tap_mutex); } break; } @@ -691,9 +1077,11 @@ handle_events(struct libinput *li, struct movement *m) } void set_device_ignore(const char* node, bool ignore) { - struct raw_multitouch_event *rme = g_hash_table_lookup(ev_table, node); + g_mutex_lock(&tap_mutex); + struct raw_multitouch_event *rme = ev_table ? g_hash_table_lookup(ev_table, node) : NULL; if (rme) { g_debug("[gesture] set device ignore: %s", ignore ? "true" : "false"); rme->ignore = ignore; } + g_mutex_unlock(&tap_mutex); } diff --git a/system/gesture1/core.h b/system/gesture1/core.h index ead194535..69b23f73c 100644 --- a/system/gesture1/core.h +++ b/system/gesture1/core.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -29,6 +29,8 @@ #define GESTURE_DIRECTION_OUT 15 int start_loop(int verbose, double distance); +void prepare_loop(void); +int wait_loop_ready(void); void quit_loop(void); void set_timer_duration(int duration); void set_timer_short_duration(int duration); diff --git a/system/gesture1/gesture.go b/system/gesture1/gesture.go index a6a0ab79c..be78e01e5 100644 --- a/system/gesture1/gesture.go +++ b/system/gesture1/gesture.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -12,6 +12,9 @@ package gesture1 import "C" import ( + "errors" + "sync" + dbus "github.com/godbus/dbus/v5" "github.com/linuxdeepin/dde-daemon/loader" "github.com/linuxdeepin/go-lib/dbusutil" @@ -124,7 +127,8 @@ func (t TouchDirection) String() string { } type Manager struct { - service *dbusutil.Service + service *dbusutil.Service + loopDone chan struct{} // nolint signals *struct { @@ -205,8 +209,9 @@ type Manager struct { } var ( - _m *Manager - logger = log.NewLogger(dbusServiceName) + _m *Manager + daemonLifecycleMu sync.Mutex + logger = log.NewLogger(dbusServiceName) ) type Daemon struct { @@ -375,19 +380,30 @@ func handleTouchUpOrCancel(scalex, scaley C.double) { } func (d *Daemon) Start() error { + daemonLifecycleMu.Lock() + defer daemonLifecycleMu.Unlock() + + if _m != nil { + return errors.New("gesture daemon is already running") + } + logger.BeginTracing() logger.Info("start gesture daemon") service := loader.GetService() - _m = &Manager{ - service: service, - } - err := service.Export(dbusPath, _m) + manager := &Manager{service: service} + _m = manager + err := service.Export(dbusPath, manager) if err != nil { + _m = nil return err } err = service.RequestName(dbusServiceName) if err != nil { + if stopErr := service.StopExport(manager); stopErr != nil { + logger.Warning("stop export after request name failed:", stopErr) + } + _m = nil return err } @@ -396,22 +412,45 @@ func (d *Daemon) Start() error { logger.Warning("Failed to load gesture config:", err) conf = &Config{} } - go C.start_loop(C.int(conf.Verbose), C.double(conf.LongPressDistance)) + manager.loopDone = make(chan struct{}) + C.prepare_loop() + go func() { + C.start_loop(C.int(conf.Verbose), C.double(conf.LongPressDistance)) + close(manager.loopDone) + }() + if C.wait_loop_ready() == 0 { + <-manager.loopDone + if releaseErr := service.ReleaseName(dbusServiceName); releaseErr != nil { + logger.Warning("release name after gesture loop start failed:", releaseErr) + } + if stopErr := service.StopExport(manager); stopErr != nil { + logger.Warning("stop export after gesture loop start failed:", stopErr) + } + _m = nil + return errors.New("failed to start gesture input loop") + } return nil } func (*Daemon) Stop() error { - if _m == nil { + daemonLifecycleMu.Lock() + defer daemonLifecycleMu.Unlock() + + manager := _m + if manager == nil { return nil } C.quit_loop() + <-manager.loopDone service := loader.GetService() - err := service.StopExport(_m) + err := service.StopExport(manager) if err != nil { return err } - _m = nil + if _m == manager { + _m = nil + } return nil }