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 }