// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

package gesture1

import (
	"fmt"
	"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"
	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"
	sessionmanager "github.com/linuxdeepin/go-dbus-factory/session/org.deepin.dde.sessionmanager1"
	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"
)

//go:generate dbusutil-gen em -type Manager

const (
	dconfigappID       = "org.deepin.dde.daemon"
	dconfigGesture     = "org.deepin.dde.daemon.gesture"
	dconfigTouchScreen = "org.deepin.dde.daemon.touchscreen"

	dconfigKeyLongpressDuration    = "longpressDuration"
	dconfigKeyShortpressDuration   = "shortpressDuration"
	dconfigKeyEdgemovestopDuration = "edgemovestopDuration"
	dconfigKeyLongpressBlacklist   = "longpressBlacklist"

	dconfigKeyTouchScreenEnabled = "touchScreenEnabled"

	dconfigKeyLongPressEnable       = "longPressEnable"
	dconfigKeyOneFingerBottomEnable = "oneFingerBottomEnable"
	dconfigKeyOneFingerRightEnable  = "oneFingerRightEnable"
)

const (
	touchRightButtonReleaseAttempts = 3
	touchRightButtonReleaseInterval = 50 * time.Millisecond
)

type deviceType int32 // 设备类型(触摸屏，触摸板)

const deviceTouchScreen deviceType = iota

var _useWayland bool

func setUseWayland(value bool) {
	_useWayland = value
}

type Manager struct {
	wm                 wm.Wm
	sysDaemon          daemon.Daemon
	systemDBus         ofdbus.DBus
	systemSigLoop      *dbusutil.SignalLoop
	sessionSigLoop     *dbusutil.SignalLoop
	mu                 sync.RWMutex
	gesture            gesture.Gesture
	dock               dock.Dock
	display            display.Display
	touchScreenEnabled dconfig.Bool
	sessionmanager     sessionmanager.SessionManager

	longPressEnable       dconfig.Bool
	oneFingerBottomEnable dconfig.Bool
	oneFingerRightEnable  dconfig.Bool
	configManagerPath     dbus.ObjectPath

	dconfigGesture     *dconfig.DConfig
	dconfigTouchScreen *dconfig.DConfig

	touchRightButtonMu   sync.Mutex
	touchRightButtonDown bool
	touchRightButtonStop bool
	destroyed            atomic.Bool
}

func newManager() (*Manager, error) {
	setUseWayland(len(os.Getenv("WAYLAND_DISPLAY")) != 0)
	sessionConn, err := dbus.SessionBus()
	if err != nil {
		return nil, err
	}

	systemConn, err := dbus.SystemBus()
	if err != nil {
		return nil, err
	}

	gestureDConfig, err := dconfig.NewDConfig(dconfigappID, dconfigGesture, "")
	if err != nil {
		return nil, err
	}

	touchScreenDConfig, err := dconfig.NewDConfig(dconfigappID, dconfigTouchScreen, "")
	if err != nil {
		return nil, err
	}

	m := &Manager{
		dconfigGesture:     gestureDConfig,
		dconfigTouchScreen: touchScreenDConfig,
		wm:                 wm.NewWm(sessionConn),
		dock:               dock.NewDock(sessionConn),
		display:            display.NewDisplay(sessionConn),
		sysDaemon:          daemon.NewDaemon(systemConn),
		systemDBus:         ofdbus.NewDBus(systemConn),
		sessionmanager:     sessionmanager.NewSessionManager(sessionConn),
	}

	m.touchScreenEnabled.Bind(m.dconfigGesture, dconfigKeyTouchScreenEnabled)
	m.longPressEnable.Bind(m.dconfigGesture, dconfigKeyLongPressEnable)
	m.oneFingerBottomEnable.Bind(m.dconfigGesture, dconfigKeyOneFingerBottomEnable)
	m.oneFingerRightEnable.Bind(m.dconfigGesture, dconfigKeyOneFingerRightEnable)

	m.gesture = gesture.NewGesture(systemConn)
	m.systemSigLoop = dbusutil.NewSignalLoop(systemConn, 10)
	m.sessionSigLoop = dbusutil.NewSignalLoop(sessionConn, 10)

	return m, nil
}

func setLongPressEnable(enable bool) {
	sessionBus, err := dbus.SessionBus()
	if err != nil {
		logger.Warning(err)
		return
	}
	Obj := sessionBus.Object("org.kde.KWin", "/KWin")
	err = Obj.Call("org.kde.KWin.setEnableTouchLongPress", 0, enable).Err
	if err != nil {
		logger.Warning(err)
	}
}

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
	}
	err = m.sysDaemon.SetLongPressDuration(0, uint32(longPress))
	if err != nil {
		logger.Warning("call SetLongPressDuration failed:", err)
	}
}

func (m *Manager) syncShortPressDuration() {
	if m.destroyed.Load() {
		return
	}
	shortPress, err := m.dconfigTouchScreen.GetValueInt64(dconfigKeyShortpressDuration)
	if err != nil {
		logger.Warning(err)
		return
	}
	err = m.gesture.SetShortPressDuration(0, uint32(shortPress))
	if err != nil {
		logger.Warning("call SetShortPressDuration failed:", err)
	}
}

func (m *Manager) syncEdgeMoveStopDuration() {
	if m.destroyed.Load() {
		return
	}
	edgemovestopDuration, err := m.dconfigTouchScreen.GetValueInt64(dconfigKeyEdgemovestopDuration)
	if err != nil {
		logger.Warning(err)
		return
	}
	err = m.gesture.SetEdgeMoveStopDuration(0, uint32(edgemovestopDuration))
	if err != nil {
		logger.Warning("call SetEdgeMoveStopDuration failed:", err)
	}
}

func (m *Manager) syncTouchscreenParameters() {
	m.syncLongPressDuration()
	m.syncShortPressDuration()
	m.syncEdgeMoveStopDuration()
}

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()
	})

	m.touchScreenEnabled.SetNotifyChangedFunc(func(value interface{}) {
		if m.destroyed.Load() {
			return
		}
		enable, ok := value.(bool)
		if !ok {
			logger.Warningf("invalid touchScreenEnabled value type %T", value)
			return
		}
		if !enable {
			m.releaseTouchRightButtonWithLog("touch screen disabled")
		}
	})

	if _useWayland {
		setLongPressEnable(m.longPressEnable.Get())
	}
	m.longPressEnable.SetNotifyChangedFunc(func(value interface{}) {
		if m.destroyed.Load() {
			return
		}
		enable, ok := value.(bool)
		if !ok {
			logger.Warningf("invalid longPressEnable value type %T", value)
			return
		}
		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.Warning("connect session locked property changed failed:", err)
	}
	m.sessionSigLoop.Start()
}

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) initSystemGestureOwnerWatcher() {
	m.systemDBus.InitSignalExt(m.systemSigLoop, true)
	_, err := m.systemDBus.ConnectNameOwnerChanged(m.handleSystemGestureOwnerChanged)
	if err != nil {
		logger.Warning("connect system gesture service owner changed failed:", err)
	}
}

func (m *Manager) handleSystemGestureOwnerChanged(name, oldOwner, newOwner string) {
	if name != dbusServiceName || m.destroyed.Load() {
		return
	}

	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.initTouchscreenParameters()
	m.initSessionWatcher()

	systemConn, err := dbus.SystemBus()
	if err != nil {
		logger.Error(err)
	}
	err = dbusutil.NewMatchRuleBuilder().Type("signal").
		PathNamespace(string(m.configManagerPath)).
		Interface("org.desktopspec.ConfigManager.Manager").
		Member("valueChanged").Build().AddTo(systemConn)
	if err != nil {
		logger.Warning(err)
	}

	m.systemSigLoop.Start()
	m.initSystemGestureOwnerWatcher()
	m.gesture.InitSignalExt(m.systemSigLoop, true)
	m.syncTouchscreenParameters()
	_, err = m.gesture.ConnectEvent(func(name string, direction string, fingers int32) {
		if m.destroyed.Load() {
			return
		}
		if !isTouchRightButtonEvent(name, direction) {
			return
		}
		if err := m.handleTouchRightButton(direction); err != nil {
			logger.Error("handle touch right button failed:", err)
		}
	})

	if err != nil {
		logger.Error("connect gesture event failed:", err)
	}

	_, err = m.gesture.ConnectTouchEdgeMoveStopLeave(func(direction string, scaleX float64, scaleY float64, duration int32) {
		should, err := m.shouldHandleEvent(deviceTouchScreen)
		if err != nil {
			logger.Error("shouldHandleEvent failed:", err)
			return
		}
		if !should {
			return
		}

		context, pointFn, err := m.getTouchScreenRotationContext()
		if err != nil {
			logger.Error("getTouchScreenRotationContext failed:", err)
		}
		p := &point{X: scaleX, Y: scaleY}
		pointFn(p)

		err = m.handleTouchEdgeMoveStopLeave(context, direction, p, duration)
		if err != nil {
			logger.Error("handleTouchEdgeMoveStopLeave failed:", err)
		}
	})
	if err != nil {
		logger.Error("connect TouchEdgeMoveStopLeave failed:", err)
	}

	_, err = m.gesture.ConnectTouchEdgeEvent(func(direction string, scaleX float64, scaleY float64) {
		should, err := m.shouldHandleEvent(deviceTouchScreen)
		if err != nil {
			logger.Error("shouldHandleEvent failed:", err)
			return
		}
		if !should {
			return
		}
		context, pointFn, err := m.getTouchScreenRotationContext()
		if err != nil {
			logger.Error("getTouchScreenRotationContext failed:", err)
		}
		p := &point{X: scaleX, Y: scaleY}
		pointFn(p)
		err = m.handleTouchEdgeEvent(context, direction, p)
		if err != nil {
			logger.Error("handleTouchEdgeEvent failed:", err)
		}
	})
	if err != nil {
		logger.Error("connect handleTouchEdgeEvent failed:", err)
	}

	_, err = m.gesture.ConnectTouchMovementEvent(func(direction string, fingers int32, startScaleX float64, startScaleY float64, endScaleX float64, endScaleY float64) {
		should, err := m.shouldHandleEvent(deviceTouchScreen)
		if err != nil {
			logger.Error("shouldHandleEvent failed:", err)
			return
		}
		if !should {
			return
		}

		context, pointFn, err := m.getTouchScreenRotationContext()
		if err != nil {
			logger.Error("getTouchScreenRotationContext failed:", err)
		}

		startP := &point{X: startScaleX, Y: startScaleY}
		endP := &point{X: endScaleX, Y: endScaleY}
		pointFn(startP)
		pointFn(endP)

		err = m.handleTouchMovementEvent(context, direction, fingers, startP, endP)
		if err != nil {
			logger.Error("handleTouchMovementEvent failed:", err)
		}
	})
	if err != nil {
		logger.Error("connect handleTouchMovementEvent failed:", err)
	}
}

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 (name != "touch right button" || direction != "up") && isKbdAlreadyGrabbed() {
		// 多任务窗口下，不应该忽略手势操作
		isShowMultiTask, err := m.wm.GetMultiTaskingStatus(0)
		if err != nil {
			logger.Warning(err)
		} else if isShowMultiTask && name == "swipe" {
			logger.Debug("should not ignore swipe event, because we are in multi task")
			return false
		}
		logger.Debug("another process grabbed keyboard, not exec action")
		return true
	}

	// TODO(jouyouyun): improve touch right button handler
	if name == "touch right button" {
		// filter google chrome
		blackList, err := m.dconfigTouchScreen.GetValueStringList(dconfigKeyLongpressBlacklist)
		if err != nil {
			logger.Warning(blackList)
		}
		if isInWindowBlacklist(getCurrentActionWindowCmd(), blackList) {
			logger.Debug("the current active window in blacklist")
			return true
		}
	} else if strings.HasPrefix(name, "touch") {
		return true
	}

	return false
}

type TouchScreensRotation uint16

// counterclockwise
const (
	Normal       TouchScreensRotation = 1
	Rotation_90  TouchScreensRotation = 2
	Rotation_180 TouchScreensRotation = 4
	Rotation_270 TouchScreensRotation = 8
)

// 获取触摸屏的旋转
func (m *Manager) getTouchScreenRotation() (display.Monitor, TouchScreensRotation) {
	// 读取触屏列表，取第一个触屏（目前触摸手势事件中不包含所属屏幕，因此不支持多个触摸屏）
	touchScreens, err := m.display.TouchscreensV2().Get(0)
	if err != nil {
		logger.Warning(err)
	}

	// 读取触摸屏映射
	touchMap, err := m.display.TouchMap().Get(0)
	if err != nil {
		logger.Warning(err)
	}

	// 读取触摸屏的名字
	var touchScreen string
	if len(touchScreens) > 0 && len(touchMap) > 0 {
		touchScreen = touchMap[touchScreens[0].UUID]
	}

	// 读取失败，把主屏当做触摸屏
	if touchScreen == "" {
		logger.Warning("failed to find the touch screen, assume the primary as the touch screen")
		touchScreen, err = m.display.Primary().Get(0)
		if err != nil {
			logger.Warning(err)
		}
	}

	// 遍历显示器，查找触摸屏的旋转角度
	monitors, err := m.display.Monitors().Get(0)
	if err != nil {
		logger.Warning(err)
	}
	sessionBus, err := dbus.SessionBus()
	if err != nil {
		logger.Warning(err)
		return nil, Normal
	}
	for _, path := range monitors {
		monitor, err := display.NewMonitor(sessionBus, path)
		if err != nil {
			logger.Warning(err)
			continue
		}

		name, err := monitor.Name().Get(0)
		if err != nil {
			logger.Warning(err)
			continue
		}

		if name == touchScreen {
			rotation, err := monitor.Rotation().Get(0)
			if err != nil {
				logger.Warning(err)
				break
			}
			return monitor, TouchScreensRotation(rotation)
		}
	}

	// 查找失败，当做没有旋转
	return nil, Normal
}

// struct point represents a point on a touchScreen
// X is a float64 in [0,1], which is the horizontal index
// Y is a float64 in [0,1], which is the vertical index
// left-top corner represents in struct point is{X:0,Y:0}
type point struct {
	X float64
	Y float64
}

// struct touchEventContext is a struct try to handle the context of touchScreen Gesture after rotation
// for example after Rotation_90 context.top is "left", and context.screenHeight is always the vertical height of screen
// see func getTouchScreenRotationContext for details
type touchEventContext struct {
	top, bot, left, right     string
	screenWidth, screenHeight uint16
}

// func getTouchScreenRotationContext return a context represents the current touchScreen's rotation, and a func to transform point
func (m *Manager) getTouchScreenRotationContext() (context *touchEventContext, pointTransformFn func(*point), err error) {
	monitor, rotation := m.getTouchScreenRotation()

	var screenWidth, screenHeight uint16
	if monitor == nil { // 如果获取失败则当作用户只有一个显示屏, 直接使用 x 的画布大小当作触摸屏大小
		screenWidth, err = m.display.ScreenWidth().Get(0)
		if err != nil {
			logger.Error("get display.ScreenWidth failed:", err)
			return
		}
		screenHeight, err = m.display.ScreenHeight().Get(0)
		if err != nil {
			logger.Error("get display.ScreenWidth failed:", err)
			return
		}
	} else {
		screenWidth, err = monitor.Width().Get(0)
		if err != nil {
			logger.Error("get monitor.Width failed:", err)
			return
		}
		screenHeight, err = monitor.Height().Get(0)
		if err != nil {
			logger.Error("get monitor.Height failed:", err)
			return
		}
	}

	pointFn := func(p *point) {}
	top, bot, left, right := "top", "bot", "left", "right"
	switch rotation {
	case Rotation_90:
		top, bot, left, right = "left", "right", "bot", "top"
		screenHeight, screenWidth = screenWidth, screenHeight
		pointFn = func(p *point) {
			p.X, p.Y = 1-p.Y, p.X
		}
	case Rotation_180:
		top, bot, left, right = "bot", "top", "right", "left"
		pointFn = func(p *point) {
			p.X, p.Y = 1-p.X, 1-p.Y
		}
	case Rotation_270:
		top, bot, left, right = "right", "left", "top", "bot"
		screenHeight, screenWidth = screenWidth, screenHeight
		pointFn = func(p *point) {
			p.X, p.Y = p.Y, 1-p.X
		}
	}
	context = &touchEventContext{
		screenWidth:  screenWidth,
		screenHeight: screenHeight,
		top:          top,
		bot:          bot,
		left:         left,
		right:        right,
	}
	pointTransformFn = pointFn
	return
}

// param @edge: swipe to touchscreen edge
// edge: 该手势来自屏幕的哪条边
// p:    该手势的终点
func (m *Manager) handleTouchEdgeMoveStopLeave(context *touchEventContext, edge string, p *point, duration int32) error {
	logger.Debugf("handleTouchEdgeMoveStopLeave: context:%+v edge:%s p: %+v", *context, edge, *p)

	if edge == context.bot && m.oneFingerBottomEnable.Get() {
		position, err := m.dock.Position().Get(0)
		if err != nil {
			logger.Error("get dock.Position failed:", err)
			return err
		}

		if position >= 0 {
			rect, err := m.dock.FrontendWindowRect().Get(0)
			if err != nil {
				logger.Error("get dock.FrontendWindowRect failed:", err)
				return err
			}

			var dockPly uint32 = 0
			switch position {
			case positionTop, positionBottom:
				dockPly = rect.Height
			case positionRight, positionLeft:
				dockPly = rect.Width
			}

			if (1-p.Y)*float64(context.screenHeight) > float64(dockPly) {
				logger.Debug("show muti task")
				return m.showMultiTaskingWithLaunchType(eventLaunchTypeTouchScreen)
			}
		}
	}
	return nil
}

func (m *Manager) showWidgets(show bool) error {
	sessionBus, err := dbus.SessionBus()
	if err != nil {
		logger.Warning(err)
		return err
	}
	obj := sessionBus.Object("org.deepin.dde.shell", "/org/deepin/dde/shell/notification/center")
	if show {
		err = obj.Call("org.deepin.dde.shell.notification.center.Show", 0).Err
	} else {
		err = obj.Call("org.deepin.dde.shell.notification.center.Hide", 0).Err
	}
	if err != nil {
		logger.Warning(err)
	}
	return err
}

// edge: 该手势来自屏幕的哪条边
// p:    该手势的终点
func (m *Manager) handleTouchEdgeEvent(context *touchEventContext, edge string, p *point) error {
	logger.Debugf("handleTouchEdgeEvent: context:%+v edge:%s p:%+v", *context, edge, *p)
	switch edge {
	case context.left:
	case context.right:
		if (1-p.X)*float64(context.screenWidth) > 100 && m.oneFingerRightEnable.Get() {
			return m.showWidgets(true)
		}
	}
	return nil
}

// direction: 该手势的方向
// fingers:   手指的数量
// startP:    该手势的起点
// endP:      该手势的终点
func (m *Manager) handleTouchMovementEvent(context *touchEventContext, direction string, fingers int32, startP *point, endP *point) error {
	logger.Debugf("handleTouchMovementEvent: context:%+v direction:%s startP:%+v endP:%+v", *context, direction, *startP, *endP)

	if fingers == 1 {
		// sensitivity check
		// TODO maybe write a function for this
		sensitivityThreshold := 0.05

		if math.Abs(startP.X-endP.X) < sensitivityThreshold {
			logger.Debug("sensitivity check fail, gesture will not be triggered")
			return nil
		}

		switch direction {
		case context.left:
		case context.right:
			if m.oneFingerRightEnable.Get() {
				return m.showWidgets(false)
			}
		}
	}

	return nil
}

// touchpad double click down
func (m *Manager) handleDbclickDown(fingers int32) error {
	if fingers == 3 {
		return m.wm.TouchToMove(0, 0, 0)
	}
	return nil
}

// touchpad swipe move
func (m *Manager) handleSwipeMoving(fingers int32, accelX float64, accelY float64) error {
	if fingers == 3 {
		return m.wm.TouchToMove(0, int32(accelX), int32(accelY))
	}
	return nil
}

// touchpad swipe stop or interrupted
func (m *Manager) handleSwipeStop(fingers int32) error {
	if fingers == 3 {
		return m.wm.ClearMoveStatus(0)
	}
	return nil
}

// 多用户存在，防止非当前用户响应触摸屏手势
func (m *Manager) shouldHandleEvent(devType deviceType) (bool, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	switch devType {
	case deviceTouchScreen:
		if !m.touchScreenEnabled.Get() {
			logger.Debug("touch screen is disabled, do not handle touchscreen gesture event")
			return false, nil
		}
	default:
		logger.Warningf("Unknown device type: %v, do not handle gesture event", devType)
		return false, nil
	}

	isLocked, err := m.sessionmanager.Locked().Get(0)
	if err != nil {
		return false, fmt.Errorf("get login1 session locked failed: %v", err)
	}

	if isLocked {
		return false, nil
	}

	currentSessionPath, err := m.sessionmanager.CurrentSessionPath().Get(0)
	if err != nil {
		return false, fmt.Errorf("get login1 session path failed: %v", err)
	}

	if !isSessionActive(currentSessionPath) {
		return false, nil
	}

	return true, nil
}
