├── debian ├── install ├── source │ └── format ├── manpages ├── prerm ├── rules ├── postinst ├── copyright ├── startdde.1 ├── deepin-fix-xauthority-perm.1 └── control ├── display ├── testdata │ ├── builtin-monitor │ ├── display_v3.json │ ├── display_v4.json │ └── display_v5.json ├── brightness │ └── t1_test.go ├── utils │ └── utils.go ├── manager_test.go ├── greeter.go ├── config_test.go ├── monitor_test.go ├── touchscreen_test.go ├── handle_event.go ├── config.go ├── exported_methods_auto.go ├── display.go ├── config_v4.go ├── display_test.go ├── config_v3_3.go ├── touchscreen.go └── brightness.go ├── .tx ├── deepin.conf └── config ├── .mailmap ├── xsettings ├── testdata │ ├── plymouth-theme.ini │ └── firefox │ │ └── xxx.default │ │ ├── prefs_none.js │ │ ├── prefs.js │ │ └── prefs_multi.js ├── exported_methods_auto.go ├── utils_gsettings.go ├── xsettings_setting.go ├── xsettings_xresource.go ├── xsettings_reader.go ├── xsettings_dpi.go ├── utils_dconfig.go ├── xsettings_writer.go └── xsettings_ifc.go ├── misc ├── filter.conf ├── lightdm.conf ├── po │ ├── locale_config.ini │ ├── startdde.pot │ ├── zh_HK.po │ ├── zh_CN.po │ ├── zh_TW.po │ ├── ca.po │ ├── hu.po │ ├── sq.po │ ├── fi.po │ ├── hr.po │ ├── nl.po │ ├── bo.po │ ├── az.po │ ├── pt.po │ ├── tr.po │ ├── pt_BR.po │ ├── sl.po │ ├── ug.po │ ├── cs.po │ ├── sr.po │ ├── es.po │ ├── pl.po │ └── uk.po ├── systemd_task │ └── dde-display-task-refresh-brightness.service ├── dsettings │ └── org.deepin.Display.json └── schemas │ └── com.deepin.dde.display.gschema.xml ├── .gitlab-ci.yml ├── wl_display ├── output_device_handler.go ├── display.go ├── config_v3_3.go ├── config_v3_3_test.go ├── exported_methods_auto.go ├── display_test.go ├── rect.go ├── handle_event.go ├── wl.go └── config.go ├── .github └── workflows │ ├── call-chatOps.yml │ ├── call-commitlint.yml │ ├── call-build-distribution.yml │ ├── call-auto-tag.yml │ ├── call-clacheck.yml │ ├── backup-to-gitlab.yml │ └── call-license-check.yml ├── rpm ├── chinese.json.patch ├── default.json.patch ├── main.go.patch ├── Makefile.patch └── startdde.spec ├── main_test.go ├── cmd ├── wl_display_daemon │ └── wl_display_daemon.go └── fix-xauthority-perm │ └── main.go ├── .gitignore ├── go.mod ├── .obs └── workflows.yml ├── archlinux └── PKGBUILD ├── .reuse └── dep5 ├── README.zh_CN.md ├── README.md ├── Makefile └── main.go /debian/install: -------------------------------------------------------------------------------- 1 | usr 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /display/testdata/builtin-monitor: -------------------------------------------------------------------------------- 1 | eDP-1 -------------------------------------------------------------------------------- /.tx/deepin.conf: -------------------------------------------------------------------------------- 1 | [transifex] 2 | branch = m20 -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Xu Fasheng 2 | -------------------------------------------------------------------------------- /xsettings/testdata/plymouth-theme.ini: -------------------------------------------------------------------------------- 1 | [Daemon] 2 | Theme = testxxx 3 | -------------------------------------------------------------------------------- /debian/manpages: -------------------------------------------------------------------------------- 1 | debian/startdde.1 2 | debian/deepin-fix-xauthority-perm.1 3 | -------------------------------------------------------------------------------- /misc/filter.conf: -------------------------------------------------------------------------------- 1 | [1002:6611] 2 | 1920*1080=59.94, 30, 29.97, 25, 24, 23.98 3 | 1680*945=60.02 4 | 5 | [1002:6779] 6 | 1920*1080=30, 29.97, 25, 24, 23.98 7 | 8 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - remote: 'https://gitlab.deepin.io/dev-tools/letmeci/raw/master/gitlab-ci/dde.yml' 3 | 4 | variables: 5 | CPPCHECK_DISABLED: "true" 6 | -------------------------------------------------------------------------------- /wl_display/output_device_handler.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | -------------------------------------------------------------------------------- /xsettings/testdata/firefox/xxx.default/prefs_none.js: -------------------------------------------------------------------------------- 1 | # Mozilla User Preferences 2 | 3 | user_pref("toolkit.telemetry.previousBuildID", "20160803004522"); 4 | user_pref("toolkit.telemetry.reportingpolicy.firstRun", false); 5 | -------------------------------------------------------------------------------- /.github/workflows/call-chatOps.yml: -------------------------------------------------------------------------------- 1 | name: chatOps 2 | on: 3 | issue_comment: 4 | types: [created] 5 | 6 | jobs: 7 | chatopt: 8 | uses: linuxdeepin/.github/.github/workflows/chatOps.yml@master 9 | secrets: inherit 10 | -------------------------------------------------------------------------------- /debian/prerm: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | set -e 3 | 4 | #DEBHELPER# 5 | 6 | case "$1" in 7 | remove|deconfigure|failed-upgrade) 8 | update-alternatives --remove x-session-mananger \ 9 | /usr/bin/startdde 10 | ;; 11 | esac 12 | 13 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | minimum_perc = 80 4 | mode = developer 5 | 6 | [deepin-desktop-environment.startddepot] 7 | file_filter = misc/po/.po 8 | source_file = misc/po/startdde.pot 9 | source_lang = en 10 | type = PO 11 | -------------------------------------------------------------------------------- /xsettings/testdata/firefox/xxx.default/prefs.js: -------------------------------------------------------------------------------- 1 | # Mozilla User Preferences 2 | 3 | user_pref("layout.css.devPixelsPerPx", "1.55"); 4 | user_pref("toolkit.telemetry.previousBuildID", "20160803004522"); 5 | user_pref("toolkit.telemetry.reportingpolicy.firstRun", false); 6 | -------------------------------------------------------------------------------- /misc/lightdm.conf: -------------------------------------------------------------------------------- 1 | [LightDM] 2 | minimum-vt=1 3 | 4 | [Seat:*] 5 | xserver-command=X -background none 6 | greeter-session=lightdm-deepin-greeter 7 | user-session=deepin 8 | 9 | # in case ~/.Xauthority changed to root owned. 10 | session-cleanup-script=/usr/sbin/deepin-fix-xauthority-perm 11 | -------------------------------------------------------------------------------- /misc/po/locale_config.ini: -------------------------------------------------------------------------------- 1 | [locale] 2 | langs=["zh_CN","zh_TW", "am", "ar", "cs", "da", "de_DE", "es_419", "es_AR", "es_CL", "es_ES", "es_MX", "es", "fi", "fr", "hu", "id_ID", "it", "nl", "pl_PL", "pt_BR", "pt_PT", "ru", "sl", "tr"] 3 | locale_dir=. 4 | project_name=startdde 5 | source_dir=../../ 6 | -------------------------------------------------------------------------------- /xsettings/testdata/firefox/xxx.default/prefs_multi.js: -------------------------------------------------------------------------------- 1 | # Mozilla User Preferences 2 | 3 | #user_pref("layout.css.devPixelsPerPx", "1.555"); 4 | user_pref("layout.css.devPixelsPerPx", "1.55"); 5 | user_pref("toolkit.telemetry.previousBuildID", "20160803004522"); 6 | user_pref("toolkit.telemetry.reportingpolicy.firstRun", false); 7 | -------------------------------------------------------------------------------- /.github/workflows/call-commitlint.yml: -------------------------------------------------------------------------------- 1 | name: Call commitlint 2 | on: 3 | pull_request_target: 4 | 5 | concurrency: 6 | group: ${{ github.workflow }}-pull/${{ github.event.number }} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | check_job: 11 | uses: linuxdeepin/.github/.github/workflows/commitlint.yml@master 12 | -------------------------------------------------------------------------------- /.github/workflows/call-build-distribution.yml: -------------------------------------------------------------------------------- 1 | name: Call build-distribution 2 | on: 3 | push: 4 | paths-ignore: 5 | - ".github/workflows/**" 6 | pull_request_target: 7 | paths-ignore: 8 | - ".github/workflows/**" 9 | 10 | jobs: 11 | check_job: 12 | uses: linuxdeepin/.github/.github/workflows/build-distribution.yml@master 13 | secrets: inherit 14 | -------------------------------------------------------------------------------- /misc/systemd_task/dde-display-task-refresh-brightness.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=A task of dde-display service, to refresh brightness 3 | 4 | Wants=dde-session.target 5 | After=dde-session.target 6 | 7 | [Service] 8 | Type=oneshot 9 | ExecStart=/usr/bin/dbus-send --print-reply --dest=org.deepin.dde.Display1 /org/deepin/dde/Display1 org.deepin.dde.Display1.RefreshBrightness 10 | -------------------------------------------------------------------------------- /rpm/chinese.json.patch: -------------------------------------------------------------------------------- 1 | --- chinese.json 2020-09-14 15:38:38.520969835 +0800 2 | +++ chinese1.json 2020-09-24 20:34:52.259271975 +0800 3 | @@ -23,7 +23,7 @@ 4 | "Priority": 9, 5 | "Group": [ 6 | { 7 | - "Command": "/usr/lib/deepin-daemon/dde-session-daemon", 8 | + "Command": "/usr/libexec/deepin-daemon/dde-session-daemon", 9 | "Wait": true, 10 | "Args": [] 11 | }, 12 | -------------------------------------------------------------------------------- /rpm/default.json.patch: -------------------------------------------------------------------------------- 1 | --- default.json 2020-09-14 15:38:38.520969835 +0800 2 | +++ default1.json 2020-09-24 20:35:17.139362540 +0800 3 | @@ -13,7 +13,7 @@ 4 | "Priority": 9, 5 | "Group": [ 6 | { 7 | - "Command": "/usr/lib/deepin-daemon/dde-session-daemon", 8 | + "Command": "/usr/libexec/deepin-daemon/dde-session-daemon", 9 | "Wait": true, 10 | "Args": [] 11 | }, 12 | -------------------------------------------------------------------------------- /.github/workflows/call-auto-tag.yml: -------------------------------------------------------------------------------- 1 | name: auto tag 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened, synchronize, closed] 6 | paths: 7 | - "debian/changelog" 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-pull/${{ github.event.number }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | auto_tag: 15 | uses: linuxdeepin/.github/.github/workflows/auto-tag.yml@master 16 | secrets: inherit 17 | -------------------------------------------------------------------------------- /.github/workflows/call-clacheck.yml: -------------------------------------------------------------------------------- 1 | name: Call CLA check 2 | on: 3 | issue_comment: 4 | types: [created] 5 | pull_request_target: 6 | types: [opened, closed, synchronize] 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-pull/${{ github.event.number }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | clacheck: 14 | uses: linuxdeepin/.github/.github/workflows/cla-check.yml@master 15 | secrets: inherit 16 | -------------------------------------------------------------------------------- /.github/workflows/backup-to-gitlab.yml: -------------------------------------------------------------------------------- 1 | name: backup to gitlab 2 | on: [push] 3 | 4 | concurrency: 5 | group: ${{ github.workflow }} 6 | cancel-in-progress: true 7 | 8 | jobs: 9 | backup-to-gitlabwh: 10 | uses: linuxdeepin/.github/.github/workflows/backup-to-gitlabwh.yml@master 11 | secrets: inherit 12 | 13 | backup-to-gitee: 14 | uses: linuxdeepin/.github/.github/workflows/backup-to-gitee.yml@master 15 | secrets: inherit 16 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package main 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/linuxdeepin/go-lib/log" 12 | ) 13 | 14 | func Test_doSetLogLevel(t *testing.T) { 15 | doSetLogLevel(log.LevelDebug) 16 | assert.Equal(t, log.LevelDebug, logger.GetLogLevel()) 17 | } 18 | -------------------------------------------------------------------------------- /cmd/wl_display_daemon/wl_display_daemon.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | wl_display "github.com/linuxdeepin/startdde/wl_display" 12 | ) 13 | 14 | func main() { 15 | err := wl_display.Start() 16 | if err != nil { 17 | fmt.Println(err) 18 | os.Exit(1) 19 | } 20 | select {} 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/call-license-check.yml: -------------------------------------------------------------------------------- 1 | name: Call License and README Check 2 | on: 3 | pull_request_target: 4 | types: [opened, synchronize, reopened] 5 | 6 | permissions: 7 | pull-requests: write 8 | contents: read 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-pull/${{ github.event.number }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | license-check: 16 | uses: linuxdeepin/.github/.github/workflows/license-check.yml@master 17 | -------------------------------------------------------------------------------- /rpm/main.go.patch: -------------------------------------------------------------------------------- 1 | --- main.go.orig 2020-10-19 18:29:22.000000000 +0800 2 | +++ main.go 2020-10-19 18:30:17.741904550 +0800 3 | @@ -89,7 +89,7 @@ 4 | 5 | const ( 6 | cmdKWin = "/usr/bin/kwin_no_scale" 7 | - cmdDdeSessionDaemon = "/usr/lib/deepin-daemon/dde-session-daemon" 8 | + cmdDdeSessionDaemon = "/usr/libexec/deepin-daemon/dde-session-daemon" 9 | cmdDdeDock = "/usr/bin/dde-dock" 10 | cmdDdeDesktop = "/usr/bin/dde-desktop" 11 | ) 12 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | export GOPATH := /usr/share/gocode 4 | export GOCACHE=/tmp/gocache 5 | export GO111MODULE = off 6 | 7 | ifeq ($(DEB_BUILD_ARCH),sw_64) 8 | export AUTO_LAUNCH_CHINESE=1 9 | endif 10 | 11 | ifeq ($(DEB_BUILD_ARCH),mips64el) 12 | export AUTO_LAUNCH_CHINESE=1 13 | endif 14 | 15 | %: 16 | dh $@ 17 | 18 | override_dh_shlibdeps: 19 | dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info 20 | 21 | override_dh_auto_install: 22 | dh_auto_install --destdir=debian/tmp 23 | 24 | ifeq ($(DEB_BUILD_ARCH),sw_64) 25 | override_dh_strip: 26 | true 27 | override_dh_auto_test: 28 | true 29 | endif 30 | 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /*.gitignore*/ 2 | 3 | startdde 4 | splash_dev_test.go 5 | 6 | # test 7 | *.test 8 | test_main.go 9 | 10 | # temporary 11 | core 12 | try/ 13 | *~ 14 | *.orig 15 | .tag* 16 | tags 17 | cscope* 18 | *.sw[po] 19 | *.log 20 | *.pyc 21 | 22 | # diloagUI 23 | dialogUI/build/ 24 | dialogUI/resources/shutdown/js/powerchoosedialog.js 25 | dialogUI/resources/shutdown/js/shutdowndialog.js 26 | dialogUI/resources/shutdown/js/rebootdialog.js 27 | dialogUI/resources/shutdown/js/logoutdialog.js 28 | 29 | # CMake 30 | CMakeFiles/ 31 | CMakeCache.txt 32 | cmake_install.cmake 33 | install_manifest.txt 34 | 35 | coverage.csv 36 | cover_report 37 | 38 | .idea 39 | out/ 40 | gopath/ -------------------------------------------------------------------------------- /debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | # postinst script 4 | # see: dh_installdeb(1) 5 | #DEBHELPER# 6 | xsOptsFile=/etc/X11/Xsession.options 7 | 8 | case "$1" in 9 | configure) 10 | 11 | update-alternatives --install /usr/bin/x-session-manager x-session-manager \ 12 | /usr/bin/startdde 90 13 | if [ -f $xsOptsFile ];then 14 | sed -i '/^use-ssh-agent/d' $xsOptsFile 15 | if ! grep '^no-use-ssh-agent' $xsOptsFile >/dev/null; then 16 | echo no-use-ssh-agent >> $xsOptsFile 17 | fi 18 | fi 19 | 20 | ;; 21 | abort-upgrade|abort-remove|abort-deconfigure) 22 | ;; 23 | *) 24 | 25 | exit 1 26 | ;; 27 | esac 28 | 29 | exit 0 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /rpm/Makefile.patch: -------------------------------------------------------------------------------- 1 | --- Makefile.orig 2020-09-17 21:16:11.810066617 +0800 2 | +++ Makefile 2020-09-17 21:16:46.166065869 +0800 3 | @@ -47,8 +47,8 @@ 4 | install -Dm644 misc/lightdm.conf ${DESTDIR}${PREFIX}/share/lightdm/lightdm.conf.d/60-deepin.conf 5 | mkdir -p ${DESTDIR}${PREFIX}/share/startdde/ 6 | cp -f misc/config/* ${DESTDIR}${PREFIX}/share/startdde/ 7 | - mkdir -p ${DESTDIR}/etc/X11/Xsession.d/ 8 | - cp -f misc/Xsession.d/* ${DESTDIR}/etc/X11/Xsession.d/ 9 | + mkdir -p ${DESTDIR}/etc/X11/xinit/xinitrc.d/ 10 | + cp -f misc/Xsession.d/* ${DESTDIR}/etc/X11/xinit/xinitrc.d/ 11 | mkdir -p ${DESTDIR}/etc/profile.d/ 12 | cp -f misc/profile.d/* ${DESTDIR}/etc/profile.d/ 13 | 14 | -------------------------------------------------------------------------------- /display/brightness/t1_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package brightness 6 | 7 | import "testing" 8 | 9 | func TestFillColorRamp(t *testing.T) { 10 | const size = 1024 11 | r, g, b := initGammaRamp(size) 12 | rCp := make([]uint16, size) 13 | gCp := make([]uint16, size) 14 | bCp := make([]uint16, size) 15 | for br := 0.0; br < 1.0; br += 0.05 { 16 | for temp := 1000; temp <= 25000; temp += 1 { 17 | copy(rCp, r) 18 | copy(gCp, g) 19 | copy(bCp, b) 20 | fillColorRamp(rCp, gCp, bCp, gammaSetting{ 21 | brightness: br, 22 | temperature: temp, 23 | }) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /display/utils/utils.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package utils 6 | 7 | //func GetOutputEDID(conn *x.Conn, output randr.Output) ([]byte, error) { 8 | // atomEDID, err := conn.GetAtom("EDID") 9 | // if err != nil { 10 | // return nil, err 11 | // } 12 | // 13 | // reply, err := randr.GetOutputProperty(conn, output, 14 | // atomEDID, x.AtomInteger, 15 | // 0, 32, false, false).Reply(conn) 16 | // if err != nil { 17 | // return nil, err 18 | // } 19 | // return reply.Value, nil 20 | //} 21 | // 22 | //func GetEDIDChecksum(edid []byte) string { 23 | // if len(edid) < 128 { 24 | // return "" 25 | // } 26 | // 27 | // id, _ := libutils.SumStrMd5(string(edid[:128])) 28 | // return id 29 | //} 30 | -------------------------------------------------------------------------------- /display/manager_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | 11 | "github.com/godbus/dbus/v5" 12 | "github.com/linuxdeepin/go-lib/dbusutil" 13 | "github.com/stretchr/testify/suite" 14 | ) 15 | 16 | type UnitTestSuite struct { 17 | suite.Suite 18 | m *Manager 19 | } 20 | 21 | func (s *UnitTestSuite) SetupSuite() { 22 | var err error 23 | s.m = &Manager{} 24 | s.m.service, err = dbusutil.NewSessionService() 25 | if err != nil { 26 | s.T().Skip(fmt.Sprintf("failed to get service: %v", err)) 27 | } 28 | 29 | s.m.sysBus, err = dbus.SystemBus() 30 | if err != nil { 31 | s.T().Skip(fmt.Sprintf("failed to get service: %v", err)) 32 | } 33 | } 34 | 35 | func (s *UnitTestSuite) Test_initScreenRotation() { 36 | s.m.initScreenRotation() 37 | } 38 | 39 | func TestUnitTestSuite(t *testing.T) { 40 | suite.Run(t, new(UnitTestSuite)) 41 | } 42 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: startdde 3 | 4 | Files: * 5 | Copyright: 2017 Deepin Technology Co., Ltd. 6 | License: GPL-3+ 7 | This package is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 3 of the License, or 10 | (at your option) any later version. 11 | . 12 | This package is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | . 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see 19 | . 20 | On Debian systems, the complete text of the GNU General 21 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/linuxdeepin/startdde 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 7 | github.com/godbus/dbus/v5 v5.1.0 8 | github.com/linuxdeepin/dde-api v0.0.0-20241128100002-d1fb4aa471f5 9 | github.com/linuxdeepin/go-dbus-factory v0.0.0-20241205055755-b43db97ea584 10 | github.com/linuxdeepin/go-gir v0.0.0-20230413065249-b60cd1aca477 11 | github.com/linuxdeepin/go-lib v0.0.0-20230406092403-b4b4282fc513 12 | github.com/linuxdeepin/go-x11-client v0.0.0-20230131052004-7503e2337ee1 13 | github.com/stretchr/testify v1.8.2 14 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 15 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c 16 | ) 17 | 18 | require ( 19 | github.com/fsnotify/fsnotify v1.6.0 // indirect 20 | github.com/kr/pretty v0.2.1 // indirect 21 | github.com/kr/text v0.1.0 // indirect 22 | github.com/pmezard/go-difflib v1.0.0 // indirect 23 | github.com/stretchr/objx v0.5.0 // indirect 24 | golang.org/x/sys v0.5.0 // indirect 25 | gopkg.in/yaml.v3 v3.0.1 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /display/greeter.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "github.com/linuxdeepin/go-x11-client/ext/input" 9 | ) 10 | 11 | // 放和 greeter-display-daemon 有关的代码 12 | 13 | func (m *Manager) doXISelectEvents(evMask uint32) error { 14 | root := m.xConn.GetDefaultScreen().Root 15 | err := input.XISelectEventsChecked(m.xConn, root, []input.EventMask{ 16 | { 17 | DeviceId: input.DeviceAllMaster, 18 | Mask: []uint32{evMask}, 19 | }, 20 | }).Check(m.xConn) 21 | return err 22 | } 23 | 24 | func (m *Manager) beginMoveMouse() { 25 | if m.cursorShowed { 26 | return 27 | } 28 | err := m.doShowCursor(true) 29 | if err != nil { 30 | logger.Warning(err) 31 | } 32 | m.cursorShowed = true 33 | } 34 | 35 | func (m *Manager) beginTouch() { 36 | if !m.cursorShowed { 37 | return 38 | } 39 | err := m.doShowCursor(false) 40 | if err != nil { 41 | logger.Warning(err) 42 | } 43 | m.cursorShowed = false 44 | } 45 | 46 | func (m *Manager) doShowCursor(show bool) error { 47 | return m.mm.showCursor(show) 48 | } 49 | -------------------------------------------------------------------------------- /.obs/workflows.yml: -------------------------------------------------------------------------------- 1 | test_build: 2 | steps: 3 | - link_package: 4 | source_project: deepin:Develop:dde 5 | source_package: %{SCM_REPOSITORY_NAME} 6 | target_project: deepin:CI 7 | 8 | - configure_repositories: 9 | project: deepin:CI 10 | repositories: 11 | - name: deepin_develop 12 | paths: 13 | - target_project: deepin:CI 14 | target_repository: deepin_develop 15 | architectures: 16 | - x86_64 17 | - aarch64 18 | 19 | - name: debian 20 | paths: 21 | - target_project: deepin:CI 22 | target_repository: debian_sid 23 | architectures: 24 | - x86_64 25 | 26 | filters: 27 | event: pull_request 28 | 29 | tag_build: 30 | steps: 31 | - trigger_services: 32 | project: deepin:Unstable:dde 33 | package: %{SCM_REPOSITORY_NAME} 34 | filters: 35 | event: tag_push 36 | 37 | commit_build: 38 | steps: 39 | - trigger_services: 40 | project: deepin:Develop:dde 41 | package: %{SCM_REPOSITORY_NAME} 42 | filters: 43 | event: push 44 | -------------------------------------------------------------------------------- /debian/startdde.1: -------------------------------------------------------------------------------- 1 | .\" Hey, EMACS: -*- nroff -*- 2 | .\" 2022 UnionTech Software Technology Co., Ltd. 3 | .\" 4 | .TH "startdde" "1" "2021-3-29" "Deepin" 5 | .\" Please adjust this date whenever revising the manpage. 6 | .\" 7 | .\" Some roff macros, for reference: 8 | .\" .nh disable hyphenation 9 | .\" .hy enable hyphenation 10 | .\" .ad l left justify 11 | .\" .ad b justify to both left and right margins 12 | .\" .nf disable filling 13 | .\" .fi enable filling 14 | .\" .br insert line break 15 | .\" .sp insert n+1 empty lines 16 | .\" for manpage-specific macros, see man(7) 17 | .SH NAME 18 | startdde \- start deepin desktop environment. 19 | .SH SYNOPSIS 20 | startdde 21 | .SH DESCRIPTION 22 | Startdde binary. 23 | .PP 24 | Startdde is starter of deepin desktop environment. 25 | .SH OPTIONS 26 | .PP 27 | -h show help info 28 | .SH SEE ALSO 29 | https://github.com/linuxdeepin/startdde 30 | .SH AUTHOR 31 | .PP 32 | .B startdde 33 | is written by UnionTech Software Technology Co., Ltd. 34 | .PP 35 | This manual page was written by 36 | .MT UnionTech Software Technology Co., Ltd. 37 | UnionTech Software Technology Co., Ltd. 38 | -------------------------------------------------------------------------------- /wl_display/display.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "github.com/godbus/dbus/v5" 9 | "github.com/linuxdeepin/go-lib/dbusutil" 10 | "github.com/linuxdeepin/go-lib/log" 11 | ) 12 | 13 | var logger = log.NewLogger("daemon/wl_display") 14 | 15 | const ( 16 | dbusServiceName = "org.deepin.dde.Display1" 17 | dbusInterface = "org.deepin.dde.Display1" 18 | dbusPath = "/org/deepin/dde/Display1" 19 | ) 20 | 21 | var _dpy *Manager 22 | 23 | func Start() error { 24 | sessionBus, err := dbus.SessionBus() 25 | if err != nil { 26 | return err 27 | } 28 | service := dbusutil.NewService(sessionBus) 29 | m := newManager(service) 30 | m.init() 31 | err = service.Export(dbusPath, m) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | err = service.RequestName(dbusServiceName) 37 | if err != nil { 38 | return err 39 | } 40 | _dpy = m 41 | return nil 42 | } 43 | 44 | func SetLogLevel(level log.Priority) { 45 | logger.SetLogLevel(level) 46 | } 47 | 48 | func GetRecommendedScaleFactor() float64 { 49 | if _dpy == nil { 50 | return 1.0 51 | } 52 | return _dpy.recommendScaleFactor 53 | } 54 | -------------------------------------------------------------------------------- /debian/deepin-fix-xauthority-perm.1: -------------------------------------------------------------------------------- 1 | .\" Hey, EMACS: -*- nroff -*- 2 | .\" 2022 UnionTech Software Technology Co., Ltd. 3 | .\" 4 | .TH "deepin-fix-xauthority-perm" "1" "2021-3-29" "Deepin" 5 | .\" Please adjust this date whenever revising the manpage. 6 | .\" 7 | .\" Some roff macros, for reference: 8 | .\" .nh disable hyphenation 9 | .\" .hy enable hyphenation 10 | .\" .ad l left justify 11 | .\" .ad b justify to both left and right margins 12 | .\" .nf disable filling 13 | .\" .fi enable filling 14 | .\" .br insert line break 15 | .\" .sp insert n+1 empty lines 16 | .\" for manpage-specific macros, see man(7) 17 | .SH NAME 18 | deepin-fix-xauthority-perm \- Repair .Xauthority filemode. 19 | .SH SYNOPSIS 20 | deepin-fix-xauthority-perm 21 | .SH DESCRIPTION 22 | Deepin Fix Xauthority Perm binary. 23 | .PP 24 | Deepin Fix Xauthority Perm is the control panel of Deepin Desktop Environment. 25 | .SH OPTIONS 26 | .PP 27 | -h show help info 28 | .SH SEE ALSO 29 | https://github.com/linuxdeepin/startdde 30 | .SH AUTHOR 31 | .PP 32 | .B deepin-fix-xauthority-perm 33 | is written by UnionTech Software Technology Co., Ltd. 34 | .PP 35 | This manual page was written by 36 | .MT UnionTech Software Technology Co., Ltd. 37 | UnionTech Software Technology Co., Ltd. 38 | -------------------------------------------------------------------------------- /misc/dsettings/org.deepin.Display.json: -------------------------------------------------------------------------------- 1 | { 2 | "magic": "dsg.config.meta", 3 | "version": "1.0", 4 | "contents": { 5 | "auto-color-temperature": { 6 | "value": "6500:3500", 7 | "serial": 0, 8 | "flags": [], 9 | "name": "auto color temperature", 10 | "description": "Automatically adjust color temperature", 11 | "permissions": "readwrite", 12 | "visibility": "private" 13 | }, 14 | "default-temperature-manual": { 15 | "value": 3500, 16 | "serial": 0, 17 | "flags": [], 18 | "name": "default temperature manual", 19 | "description": "default temperature by manual", 20 | "permissions": "readwrite", 21 | "visibility": "private" 22 | }, 23 | "custom-mode-time": { 24 | "value": "22:00-7:00", 25 | "serial": 0, 26 | "flags": [], 27 | "name": "Color Temperature Custom Mode Time", 28 | "description": "Recording the time for custom mode", 29 | "permissions": "readwrite", 30 | "visibility": "private" 31 | }, 32 | "color-temperature-mode-on": { 33 | "value": 2, 34 | "serial": 0, 35 | "flags": [], 36 | "name": "Color Temperature Mode On", 37 | "description": "Recording the mode for last time", 38 | "permissions": "readwrite", 39 | "visibility": "private" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: startdde 2 | Section: x11 3 | Priority: optional 4 | Maintainer: Deepin Packages Builder 5 | Build-Depends: 6 | dde-api-dev(>> 3.16.0+), 7 | debhelper-compat (= 11), 8 | golang-github-linuxdeepin-go-lib-dev (>> 1.8.0+), 9 | golang-github-davecgh-go-spew-dev, 10 | golang-github-fsnotify-fsnotify-dev, 11 | golang-github-linuxdeepin-go-dbus-factory-dev (>> 0.6.0+), 12 | golang-github-linuxdeepin-go-x11-client-dev (>= 0.0.4), 13 | golang-github-stretchr-testify-dev, 14 | golang-go, 15 | golang-golang-x-xerrors-dev, 16 | golang-gopkg-check.v1-dev, 17 | jq, 18 | libsecret-1-dev, 19 | libxcursor-dev, 20 | libxfixes-dev, 21 | libxi-dev, 22 | pkg-config, 23 | Standards-Version: 4.5.1 24 | Homepage: http://github.com/linuxdeepin/startdde 25 | 26 | Package: startdde 27 | Architecture: any 28 | Provides: 29 | x-session-manager, 30 | Depends: 31 | dde-api, 32 | dde-daemon (>> 3.24.1+), 33 | deepin-desktop-schemas (>> 5.2.0+), 34 | deepin-proxy, 35 | gnome-keyring, 36 | libpam-gnome-keyring, 37 | procps, 38 | xinput, 39 | redshift, 40 | dde-appearance, 41 | ${misc:Depends}, 42 | ${shlibs:Depends}, 43 | Conflicts: 44 | deepin-session, 45 | deepin-wm-switcher, 46 | Breaks: 47 | deepin-desktop-schemas(<= 5.8.44), 48 | Description: start deepin desktop environment 49 | starter of deepin desktop environment. 50 | -------------------------------------------------------------------------------- /misc/po/startdde.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=CHARSET\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Language: \n" 19 | 20 | #: ../../main.go:410 ../../main.go:446 21 | #, c-format 22 | msgid "Your password will expire in %d days" 23 | msgstr "Your password will expire in %d days" 24 | 25 | #: ../../main.go:414 ../../main.go:448 26 | #, c-format 27 | msgid "%d login failures since the last successful login" 28 | msgstr "%d login failures since the last successful login" 29 | 30 | #: ../../main.go:423 31 | msgid "Login Reminder" 32 | msgstr "Login Reminder" 33 | 34 | #: ../../main.go:424 35 | msgid "Details" 36 | msgstr "Details" 37 | 38 | #: ../../main.go:444 39 | #, c-format 40 | msgid "Login time: %s" 41 | msgstr "Login time: %s" 42 | 43 | #: ../../main.go:445 44 | #, c-format 45 | msgid "Last login: %s" 46 | msgstr "Last login: %s" 47 | -------------------------------------------------------------------------------- /archlinux/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: justforlxz 2 | pkgname=startdde-git 3 | pkgver=5.9.15.1.r24.g9684bb4 4 | pkgrel=1 5 | sourcename=startdde 6 | sourcetars=("$sourcename"_"$pkgver".tar.xz vendor.tar.gz) 7 | sourcedir="$sourcename" 8 | useforobs=1 9 | pkgdesc="starter of deepin desktop environment" 10 | arch=('x86_64' 'aarch64') 11 | url="https://github.com/linuxdeepin/startdde" 12 | license=('GPL3') 13 | depends=('libgnome-keyring') 14 | makedepends=('cmake' 'coffeescript' 'deepin-api-git' 'go' 'git' 'jq') 15 | optdepends=('deepin-wm: Legacy 3D window manager' 16 | 'deepin-metacity: Legacy 2D window manager' 17 | 'deepin-kwin: Preferred window manager') 18 | provides=('startdde') 19 | conflicts=('startdde') 20 | groups=('deepin-git') 21 | source=("${sourcetars[@]}") 22 | sha512sums=('SKIP' 'SKIP') 23 | 24 | prepare() { 25 | cd $sourcedir 26 | export GOPATH="$srcdir/build:/usr/share/gocode" 27 | sed -i 's/sbin/bin/' Makefile 28 | } 29 | 30 | build() { 31 | if [[ ! -n "$useforobs" ]];then 32 | export GOFLAGS="-buildmode=pie -trimpath -modcacherw -mod=readonly" 33 | go mod tidy 34 | else 35 | export GOFLAGS="-buildmode=pie -trimpath -modcacherw -mod=vendor" 36 | mv "$srcdir"/vendor "$srcdir"/"$sourcedir"/vendor 37 | fi 38 | cd $sourcedir 39 | make 40 | } 41 | 42 | package() { 43 | cd $sourcedir 44 | make DESTDIR="$pkgdir" install 45 | } 46 | -------------------------------------------------------------------------------- /misc/po/zh_HK.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Cindy0514, 2021 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 15 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 16 | "Last-Translator: Cindy0514, 2021\n" 17 | "Language-Team: Chinese (Hong Kong) (https://www.transifex.com/linuxdeepin/teams/3617/zh_HK/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: zh_HK\n" 22 | "Plural-Forms: nplurals=1; plural=0;\n" 23 | 24 | #: ../../main.go:410 ../../main.go:446 25 | #, c-format 26 | msgid "Your password will expire in %d days" 27 | msgstr "密碼將於%d天後過期" 28 | 29 | #: ../../main.go:414 ../../main.go:448 30 | #, c-format 31 | msgid "%d login failures since the last successful login" 32 | msgstr "距離上次成功登錄,中間共計%d次登錄失敗" 33 | 34 | #: ../../main.go:423 35 | msgid "Login Reminder" 36 | msgstr "登錄提醒" 37 | 38 | #: ../../main.go:424 39 | msgid "Details" 40 | msgstr "詳情" 41 | 42 | #: ../../main.go:444 43 | #, c-format 44 | msgid "Login time: %s" 45 | msgstr "本次登錄時間:%s" 46 | 47 | #: ../../main.go:445 48 | #, c-format 49 | msgid "Last login: %s" 50 | msgstr "上次登錄時間:%s" 51 | -------------------------------------------------------------------------------- /misc/po/zh_CN.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Maggie Liu , 2021 8 | # Cindy0514, 2021 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 16 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 17 | "Last-Translator: Cindy0514, 2021\n" 18 | "Language-Team: Chinese (China) (https://www.transifex.com/linuxdeepin/teams/3617/zh_CN/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: zh_CN\n" 23 | "Plural-Forms: nplurals=1; plural=0;\n" 24 | 25 | #: ../../main.go:410 ../../main.go:446 26 | #, c-format 27 | msgid "Your password will expire in %d days" 28 | msgstr "密码将于%d天后过期" 29 | 30 | #: ../../main.go:414 ../../main.go:448 31 | #, c-format 32 | msgid "%d login failures since the last successful login" 33 | msgstr "距离上次成功登录,中间共计%d次登录失败" 34 | 35 | #: ../../main.go:423 36 | msgid "Login Reminder" 37 | msgstr "登录提醒" 38 | 39 | #: ../../main.go:424 40 | msgid "Details" 41 | msgstr "详情" 42 | 43 | #: ../../main.go:444 44 | #, c-format 45 | msgid "Login time: %s" 46 | msgstr "本次登录时间:%s" 47 | 48 | #: ../../main.go:445 49 | #, c-format 50 | msgid "Last login: %s" 51 | msgstr "上次登录时间:%s" 52 | -------------------------------------------------------------------------------- /misc/po/zh_TW.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Julian Lai , 2021 8 | # Cindy0514, 2021 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 16 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 17 | "Last-Translator: Cindy0514, 2021\n" 18 | "Language-Team: Chinese (Taiwan) (https://www.transifex.com/linuxdeepin/teams/3617/zh_TW/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: zh_TW\n" 23 | "Plural-Forms: nplurals=1; plural=0;\n" 24 | 25 | #: ../../main.go:410 ../../main.go:446 26 | #, c-format 27 | msgid "Your password will expire in %d days" 28 | msgstr "密碼將於%d天後過期" 29 | 30 | #: ../../main.go:414 ../../main.go:448 31 | #, c-format 32 | msgid "%d login failures since the last successful login" 33 | msgstr "距離上次成功登入,中間共計%d次登入失敗" 34 | 35 | #: ../../main.go:423 36 | msgid "Login Reminder" 37 | msgstr "登入提醒" 38 | 39 | #: ../../main.go:424 40 | msgid "Details" 41 | msgstr "詳細資訊" 42 | 43 | #: ../../main.go:444 44 | #, c-format 45 | msgid "Login time: %s" 46 | msgstr "本次登入時間:%s" 47 | 48 | #: ../../main.go:445 49 | #, c-format 50 | msgid "Last login: %s" 51 | msgstr "上次登入時間:%s" 52 | -------------------------------------------------------------------------------- /display/testdata/display_v3.json: -------------------------------------------------------------------------------- 1 | { 2 | "eDP12c5a5c24e4ab14126abf8dc36e7e9d4": { 3 | "Name": "", 4 | "Primary": "eDP-1", 5 | "BaseInfos": [ 6 | { 7 | "UUID": "eDP12c5a5c24e4ab14126abf8dc36e7e9d4", 8 | "Name": "eDP-1", 9 | "Enabled": true, 10 | "X": 0, 11 | "Y": 0, 12 | "Width": 1360, 13 | "Height": 768, 14 | "Rotation": 1, 15 | "Reflect": 0, 16 | "RefreshRate": 59.79899072004335, 17 | "MmWidth": 0, 18 | "MmHeight": 0 19 | } 20 | ] 21 | }, 22 | "_dde_display_config_private+HDMIf5cae317c40b01139be5af61896be0cf,eDP12c5a5c24e4ab14126abf8dc36e7e9d4": { 23 | "Name": "_dde_display_config_private", 24 | "Primary": "eDP-1", 25 | "BaseInfos": [ 26 | { 27 | "UUID": "eDP12c5a5c24e4ab14126abf8dc36e7e9d4", 28 | "Name": "eDP-1", 29 | "Enabled": true, 30 | "X": 0, 31 | "Y": 0, 32 | "Width": 1366, 33 | "Height": 768, 34 | "Rotation": 1, 35 | "Reflect": 0, 36 | "RefreshRate": 60.00471735199308, 37 | "MmWidth": 0, 38 | "MmHeight": 0 39 | }, 40 | { 41 | "UUID": "HDMIf5cae317c40b01139be5af61896be0cf", 42 | "Name": "HDMI-2", 43 | "Enabled": true, 44 | "X": 1366, 45 | "Y": 0, 46 | "Width": 1920, 47 | "Height": 1080, 48 | "Rotation": 1, 49 | "Reflect": 0, 50 | "RefreshRate": 60, 51 | "MmWidth": 0, 52 | "MmHeight": 0 53 | } 54 | ] 55 | } 56 | } -------------------------------------------------------------------------------- /misc/po/ca.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Davidmp , 2021 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 15 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 16 | "Last-Translator: Davidmp , 2021\n" 17 | "Language-Team: Catalan (https://www.transifex.com/linuxdeepin/teams/3617/ca/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: ca\n" 22 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 23 | 24 | #: ../../main.go:410 ../../main.go:446 25 | #, c-format 26 | msgid "Your password will expire in %d days" 27 | msgstr "La contrasenya vencerà d'aquí a %d dies." 28 | 29 | #: ../../main.go:414 ../../main.go:448 30 | #, c-format 31 | msgid "%d login failures since the last successful login" 32 | msgstr "%d errors d'entrada des de la darrera obertura de sessió." 33 | 34 | #: ../../main.go:423 35 | msgid "Login Reminder" 36 | msgstr "Recordatori d'entrada" 37 | 38 | #: ../../main.go:424 39 | msgid "Details" 40 | msgstr "Detalls" 41 | 42 | #: ../../main.go:444 43 | #, c-format 44 | msgid "Login time: %s" 45 | msgstr "Hora d'entrada: %s" 46 | 47 | #: ../../main.go:445 48 | #, c-format 49 | msgid "Last login: %s" 50 | msgstr "Darrera entrada: %s" 51 | -------------------------------------------------------------------------------- /misc/po/hu.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Kiss Zoltan , 2021 8 | # Béla Bajza, 2021 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 16 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 17 | "Last-Translator: Béla Bajza, 2021\n" 18 | "Language-Team: Hungarian (https://www.transifex.com/linuxdeepin/teams/3617/hu/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: hu\n" 23 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 24 | 25 | #: ../../main.go:410 ../../main.go:446 26 | #, c-format 27 | msgid "Your password will expire in %d days" 28 | msgstr "Jelszava %d nap múlva lejár" 29 | 30 | #: ../../main.go:414 ../../main.go:448 31 | #, c-format 32 | msgid "%d login failures since the last successful login" 33 | msgstr "%d bejelentkezési hiba az utolsó sikeres bejelentkezés óta" 34 | 35 | #: ../../main.go:423 36 | msgid "Login Reminder" 37 | msgstr "Bejelentkezési emlékeztető" 38 | 39 | #: ../../main.go:424 40 | msgid "Details" 41 | msgstr "Részletek" 42 | 43 | #: ../../main.go:444 44 | #, c-format 45 | msgid "Login time: %s" 46 | msgstr "Bejelentkezés ideje: %s" 47 | 48 | #: ../../main.go:445 49 | #, c-format 50 | msgid "Last login: %s" 51 | msgstr "Utolsó bejelentkezés: %s" 52 | -------------------------------------------------------------------------------- /misc/po/sq.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Besnik Bleta , 2021 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 15 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 16 | "Last-Translator: Besnik Bleta , 2021\n" 17 | "Language-Team: Albanian (https://www.transifex.com/linuxdeepin/teams/3617/sq/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: sq\n" 22 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 23 | 24 | #: ../../main.go:410 ../../main.go:446 25 | #, c-format 26 | msgid "Your password will expire in %d days" 27 | msgstr "Fjalëkalimi juaj do të skadojë pas %d ditësh" 28 | 29 | #: ../../main.go:414 ../../main.go:448 30 | #, c-format 31 | msgid "%d login failures since the last successful login" 32 | msgstr "%d dështime hyrjeje që nga hyrja e fundit e suksesshme" 33 | 34 | #: ../../main.go:423 35 | msgid "Login Reminder" 36 | msgstr "Kujtues Hyrjesh" 37 | 38 | #: ../../main.go:424 39 | msgid "Details" 40 | msgstr "Hollësi" 41 | 42 | #: ../../main.go:444 43 | #, c-format 44 | msgid "Login time: %s" 45 | msgstr "Kohë hyrjeje: %s" 46 | 47 | #: ../../main.go:445 48 | #, c-format 49 | msgid "Last login: %s" 50 | msgstr "Hyrja e fundit: %s" 51 | -------------------------------------------------------------------------------- /misc/po/fi.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Kimmo Kujansuu , 2021 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 15 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 16 | "Last-Translator: Kimmo Kujansuu , 2021\n" 17 | "Language-Team: Finnish (https://www.transifex.com/linuxdeepin/teams/3617/fi/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: fi\n" 22 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 23 | 24 | #: ../../main.go:410 ../../main.go:446 25 | #, c-format 26 | msgid "Your password will expire in %d days" 27 | msgstr "Salasanasi vanhenee %d päivän kuluttua" 28 | 29 | #: ../../main.go:414 ../../main.go:448 30 | #, c-format 31 | msgid "%d login failures since the last successful login" 32 | msgstr "%d virhettä edellisen onnistuneen kirjautumisen jälkeen" 33 | 34 | #: ../../main.go:423 35 | msgid "Login Reminder" 36 | msgstr "Kirjautumisen muistutus" 37 | 38 | #: ../../main.go:424 39 | msgid "Details" 40 | msgstr "Tiedot" 41 | 42 | #: ../../main.go:444 43 | #, c-format 44 | msgid "Login time: %s" 45 | msgstr "Kirjautumisaika: %s" 46 | 47 | #: ../../main.go:445 48 | #, c-format 49 | msgid "Last login: %s" 50 | msgstr "Viimeisin kirjautuminen: %s" 51 | -------------------------------------------------------------------------------- /misc/po/hr.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # dpnlin, 2021 8 | # Ivica Kolić , 2021 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 16 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 17 | "Last-Translator: Ivica Kolić , 2021\n" 18 | "Language-Team: Croatian (https://www.transifex.com/linuxdeepin/teams/3617/hr/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: hr\n" 23 | "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" 24 | 25 | #: ../../main.go:410 ../../main.go:446 26 | #, c-format 27 | msgid "Your password will expire in %d days" 28 | msgstr "Vaša će lozinka isteći za %d dana" 29 | 30 | #: ../../main.go:414 ../../main.go:448 31 | #, c-format 32 | msgid "%d login failures since the last successful login" 33 | msgstr "" 34 | 35 | #: ../../main.go:423 36 | msgid "Login Reminder" 37 | msgstr "Podsjetnik prijave" 38 | 39 | #: ../../main.go:424 40 | msgid "Details" 41 | msgstr "Detalji" 42 | 43 | #: ../../main.go:444 44 | #, c-format 45 | msgid "Login time: %s" 46 | msgstr "Vrijeme prijave: %s" 47 | 48 | #: ../../main.go:445 49 | #, c-format 50 | msgid "Last login: %s" 51 | msgstr "Zadnja prijava: %s" 52 | -------------------------------------------------------------------------------- /misc/po/nl.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # dragnadh, 2021 8 | # Heimen Stoffels , 2021 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 16 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 17 | "Last-Translator: Heimen Stoffels , 2021\n" 18 | "Language-Team: Dutch (https://www.transifex.com/linuxdeepin/teams/3617/nl/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: nl\n" 23 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 24 | 25 | #: ../../main.go:410 ../../main.go:446 26 | #, c-format 27 | msgid "Your password will expire in %d days" 28 | msgstr "Je wachtwoord verloopt over %d dagen" 29 | 30 | #: ../../main.go:414 ../../main.go:448 31 | #, c-format 32 | msgid "%d login failures since the last successful login" 33 | msgstr "%d mislukte aanmeldpogingen sinds de laatste aanmelding" 34 | 35 | #: ../../main.go:423 36 | msgid "Login Reminder" 37 | msgstr "Aanmeldherinnering" 38 | 39 | #: ../../main.go:424 40 | msgid "Details" 41 | msgstr "Details" 42 | 43 | #: ../../main.go:444 44 | #, c-format 45 | msgid "Login time: %s" 46 | msgstr "Aanmeldtijd: %s" 47 | 48 | #: ../../main.go:445 49 | #, c-format 50 | msgid "Last login: %s" 51 | msgstr "Recentste aanmelding: %s" 52 | -------------------------------------------------------------------------------- /misc/po/bo.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # yeshe lhamo <981611630@qq.com>, 2021 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 15 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 16 | "Last-Translator: yeshe lhamo <981611630@qq.com>, 2021\n" 17 | "Language-Team: Tibetan (https://www.transifex.com/linuxdeepin/teams/3617/bo/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: bo\n" 22 | "Plural-Forms: nplurals=1; plural=0;\n" 23 | 24 | #: ../../main.go:410 ../../main.go:446 25 | #, c-format 26 | msgid "Your password will expire in %d days" 27 | msgstr "ཉིན་%dརྗེས་གསང་ཨང་དུས་ཚད་ཡོལ་བ།" 28 | 29 | #: ../../main.go:414 ../../main.go:448 30 | #, c-format 31 | msgid "%d login failures since the last successful login" 32 | msgstr "" 33 | "ཐེངས་སྔོན་མར་ཐོ་འཇུག་བྱས་པ་ནས་བཟུང་བར་དུ་ཁྱོན་ཐེངས་%dཐོ་འཇུག་བྱེད་ཐུབ་མེད།" 34 | 35 | #: ../../main.go:423 36 | msgid "Login Reminder" 37 | msgstr "ཐོ་འཇུག་དྲན་སྐུལ།" 38 | 39 | #: ../../main.go:424 40 | msgid "Details" 41 | msgstr "གནས་ཚུལ་ཞིབ་ཕྲ།" 42 | 43 | #: ../../main.go:444 44 | #, c-format 45 | msgid "Login time: %s" 46 | msgstr "ཐེངས་འདིའི་ཐོ་འཇུག་དུས་ཚོད།%s" 47 | 48 | #: ../../main.go:445 49 | #, c-format 50 | msgid "Last login: %s" 51 | msgstr "ཐེངས་སྔ་མའི་ཐོ་འཇུག་དུས་ཚོད།%s" 52 | -------------------------------------------------------------------------------- /misc/po/az.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Mehman Bashirov , 2021 8 | # Xəyyam Qocayev , 2021 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 16 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 17 | "Last-Translator: Xəyyam Qocayev , 2021\n" 18 | "Language-Team: Azerbaijani (https://www.transifex.com/linuxdeepin/teams/3617/az/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: az\n" 23 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 24 | 25 | #: ../../main.go:410 ../../main.go:446 26 | #, c-format 27 | msgid "Your password will expire in %d days" 28 | msgstr "Şifrəniz, %dgün ərzində etibarlı olacaq" 29 | 30 | #: ../../main.go:414 ../../main.go:448 31 | #, c-format 32 | msgid "%d login failures since the last successful login" 33 | msgstr "Son uğurlu girişdən bəri %d girişi alınmadı" 34 | 35 | #: ../../main.go:423 36 | msgid "Login Reminder" 37 | msgstr "Sistemə giriş xəbərdarlığı" 38 | 39 | #: ../../main.go:424 40 | msgid "Details" 41 | msgstr "Ayrıntılar" 42 | 43 | #: ../../main.go:444 44 | #, c-format 45 | msgid "Login time: %s" 46 | msgstr "Giriş vaxtı: %s" 47 | 48 | #: ../../main.go:445 49 | #, c-format 50 | msgid "Last login: %s" 51 | msgstr "Sonuncu giriş: %s" 52 | -------------------------------------------------------------------------------- /misc/po/pt.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Ricardo Simões , 2021 8 | # Galvanize87 , 2021 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 16 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 17 | "Last-Translator: Galvanize87 , 2021\n" 18 | "Language-Team: Portuguese (https://www.transifex.com/linuxdeepin/teams/3617/pt/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: pt\n" 23 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 24 | 25 | #: ../../main.go:410 ../../main.go:446 26 | #, c-format 27 | msgid "Your password will expire in %d days" 28 | msgstr "A sua password irá expirar em %d dias" 29 | 30 | #: ../../main.go:414 ../../main.go:448 31 | #, c-format 32 | msgid "%d login failures since the last successful login" 33 | msgstr "%d falhas de login desde o último login bem sucedido" 34 | 35 | #: ../../main.go:423 36 | msgid "Login Reminder" 37 | msgstr "Dica de Login" 38 | 39 | #: ../../main.go:424 40 | msgid "Details" 41 | msgstr "Detalhes" 42 | 43 | #: ../../main.go:444 44 | #, c-format 45 | msgid "Login time: %s" 46 | msgstr "Tempo de login: %s" 47 | 48 | #: ../../main.go:445 49 | #, c-format 50 | msgid "Last login: %s" 51 | msgstr "Último login: %s" 52 | -------------------------------------------------------------------------------- /misc/po/tr.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Ümit Solmaz, 2021 8 | # Kaya Zeren , 2021 9 | # abc Def , 2021 10 | # 11 | #, fuzzy 12 | msgid "" 13 | msgstr "" 14 | "Project-Id-Version: PACKAGE VERSION\n" 15 | "Report-Msgid-Bugs-To: \n" 16 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 17 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 18 | "Last-Translator: abc Def , 2021\n" 19 | "Language-Team: Turkish (https://www.transifex.com/linuxdeepin/teams/3617/tr/)\n" 20 | "MIME-Version: 1.0\n" 21 | "Content-Type: text/plain; charset=UTF-8\n" 22 | "Content-Transfer-Encoding: 8bit\n" 23 | "Language: tr\n" 24 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 25 | 26 | #: ../../main.go:410 ../../main.go:446 27 | #, c-format 28 | msgid "Your password will expire in %d days" 29 | msgstr "Parolanızın süresi gün içinde %ddolacak" 30 | 31 | #: ../../main.go:414 ../../main.go:448 32 | #, c-format 33 | msgid "%d login failures since the last successful login" 34 | msgstr "%d son başarılı girişten bu yana giriş hataları" 35 | 36 | #: ../../main.go:423 37 | msgid "Login Reminder" 38 | msgstr "Giriş Hatırlatıcı" 39 | 40 | #: ../../main.go:424 41 | msgid "Details" 42 | msgstr "Ayrıntılar" 43 | 44 | #: ../../main.go:444 45 | #, c-format 46 | msgid "Login time: %s" 47 | msgstr "Giriş zamanı: %s" 48 | 49 | #: ../../main.go:445 50 | #, c-format 51 | msgid "Last login: %s" 52 | msgstr "Son giriş: %s" 53 | -------------------------------------------------------------------------------- /misc/po/pt_BR.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Joel Queiroz , 2021 8 | # Raphael Miquelis , 2021 9 | # Marcelo Filho, 2021 10 | # 11 | #, fuzzy 12 | msgid "" 13 | msgstr "" 14 | "Project-Id-Version: PACKAGE VERSION\n" 15 | "Report-Msgid-Bugs-To: \n" 16 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 17 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 18 | "Last-Translator: Marcelo Filho, 2021\n" 19 | "Language-Team: Portuguese (Brazil) (https://www.transifex.com/linuxdeepin/teams/3617/pt_BR/)\n" 20 | "MIME-Version: 1.0\n" 21 | "Content-Type: text/plain; charset=UTF-8\n" 22 | "Content-Transfer-Encoding: 8bit\n" 23 | "Language: pt_BR\n" 24 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 25 | 26 | #: ../../main.go:410 ../../main.go:446 27 | #, c-format 28 | msgid "Your password will expire in %d days" 29 | msgstr "Sua senha atingirá o prazo de validade em %d dias" 30 | 31 | #: ../../main.go:414 ../../main.go:448 32 | #, c-format 33 | msgid "%d login failures since the last successful login" 34 | msgstr "%d falhas de login desde o último login bem sucedido" 35 | 36 | #: ../../main.go:423 37 | msgid "Login Reminder" 38 | msgstr "Lembrete de login" 39 | 40 | #: ../../main.go:424 41 | msgid "Details" 42 | msgstr "Detalhes" 43 | 44 | #: ../../main.go:444 45 | #, c-format 46 | msgid "Login time: %s" 47 | msgstr "Tempo de uso: %s" 48 | 49 | #: ../../main.go:445 50 | #, c-format 51 | msgid "Last login: %s" 52 | msgstr "Último login: %s" 53 | -------------------------------------------------------------------------------- /misc/po/sl.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Gorzy Gorup , 2021 8 | # Arnold Marko , 2021 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 16 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 17 | "Last-Translator: Arnold Marko , 2021\n" 18 | "Language-Team: Slovenian (https://www.transifex.com/linuxdeepin/teams/3617/sl/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: sl\n" 23 | "Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n" 24 | 25 | #: ../../main.go:410 ../../main.go:446 26 | #, c-format 27 | msgid "Your password will expire in %d days" 28 | msgstr "Geslo bo poteklo v %d dnevih" 29 | 30 | #: ../../main.go:414 ../../main.go:448 31 | #, c-format 32 | msgid "%d login failures since the last successful login" 33 | msgstr "%d neuspelih prijav od zadnje uspešne prijave" 34 | 35 | #: ../../main.go:423 36 | msgid "Login Reminder" 37 | msgstr "Opomnik prijave" 38 | 39 | #: ../../main.go:424 40 | msgid "Details" 41 | msgstr "Podrobnosti" 42 | 43 | #: ../../main.go:444 44 | #, c-format 45 | msgid "Login time: %s" 46 | msgstr "Čas prijave: %s" 47 | 48 | #: ../../main.go:445 49 | #, c-format 50 | msgid "Last login: %s" 51 | msgstr "Zadnja prijava: %s" 52 | -------------------------------------------------------------------------------- /misc/po/ug.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # 阿迪力 买买提明 <251078110@qq.com>, 2021 8 | # مۇختەر مەخمۇت , 2021 9 | # Cindy0514, 2021 10 | # 11 | #, fuzzy 12 | msgid "" 13 | msgstr "" 14 | "Project-Id-Version: PACKAGE VERSION\n" 15 | "Report-Msgid-Bugs-To: \n" 16 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 17 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 18 | "Last-Translator: Cindy0514, 2021\n" 19 | "Language-Team: Uyghur (https://www.transifex.com/linuxdeepin/teams/3617/ug/)\n" 20 | "MIME-Version: 1.0\n" 21 | "Content-Type: text/plain; charset=UTF-8\n" 22 | "Content-Transfer-Encoding: 8bit\n" 23 | "Language: ug\n" 24 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 25 | 26 | #: ../../main.go:410 ../../main.go:446 27 | #, c-format 28 | msgid "Your password will expire in %d days" 29 | msgstr "پارولىڭىزنىڭ %d كۈندىن كېيىن ۋاقتى ئۆتىدۇ" 30 | 31 | #: ../../main.go:414 ../../main.go:448 32 | #, c-format 33 | msgid "%d login failures since the last successful login" 34 | msgstr "" 35 | "ئالدىنقى قېتىم كىرگەندىن ھازىرغىچە، ئارىلىقتا %d قېتىم كىرىش مەغلۇپ بولدى" 36 | 37 | #: ../../main.go:423 38 | msgid "Login Reminder" 39 | msgstr "كىرىش ئەسكەرتمىسى" 40 | 41 | #: ../../main.go:424 42 | msgid "Details" 43 | msgstr "تەپسىلاتى" 44 | 45 | #: ../../main.go:444 46 | #, c-format 47 | msgid "Login time: %s" 48 | msgstr "بۇ قېتىم كىرگەن ۋاقىت: %s" 49 | 50 | #: ../../main.go:445 51 | #, c-format 52 | msgid "Last login: %s" 53 | msgstr "ئالدىنقى قېتىم كىرگەن ۋاقىت: %s" 54 | -------------------------------------------------------------------------------- /display/config_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | var ( 14 | configPath_v3 = "./testdata/display_v3.json" 15 | configPath_v4 = "./testdata/display_v4.json" 16 | configPath_v5 = "./testdata/display_v5.json" 17 | configPath_tmp = "./testdata/display_tmp.json" 18 | ) 19 | 20 | func TestConfig(t *testing.T) { 21 | _, err := loadConfigV3D3(configPath_v3) 22 | require.Nil(t, err) 23 | 24 | _, err = loadConfigV4(configPath_v4) 25 | require.Nil(t, err) 26 | 27 | config, err := loadConfigV5V6(configPath_v5) 28 | require.Nil(t, err) 29 | 30 | screenConfig := config.ConfigV5["HDMI-1bc06f293ee6bfb16fd813648741f8ac3,eDP-12fd580d2dc41168dce2efb1bf19adb54"] 31 | require.NotNil(t, screenConfig) 32 | 33 | modeConfig := screenConfig.getModeConfigs(DisplayModeExtend) 34 | require.NotNil(t, modeConfig) 35 | 36 | monitors := modeConfig.Monitors 37 | require.NotEqual(t, len(monitors), 0) 38 | 39 | monitorConfig := getMonitorConfigByUuid(monitors, "eDP-12fd580d2dc41168dce2efb1bf19adb54") 40 | require.NotNil(t, monitorConfig) 41 | 42 | primaryMonitor := getMonitorConfigPrimary(monitors) 43 | require.Equal(t, primaryMonitor.UUID, monitorConfig.UUID) 44 | 45 | setMonitorConfigsPrimary(monitors, "HDMI-1bc06f293ee6bfb16fd813648741f8ac3") 46 | primaryMonitor = getMonitorConfigPrimary(monitors) 47 | require.Equal(t, primaryMonitor.UUID, "HDMI-1bc06f293ee6bfb16fd813648741f8ac3") 48 | 49 | _, err = loadConfigV5V6(configPath_v5) 50 | require.Nil(t, err) 51 | } 52 | -------------------------------------------------------------------------------- /misc/po/cs.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Pavel Borecki , 2021 8 | # fri, 2021 9 | # Tomáš Hurta, 2021 10 | # 11 | #, fuzzy 12 | msgid "" 13 | msgstr "" 14 | "Project-Id-Version: PACKAGE VERSION\n" 15 | "Report-Msgid-Bugs-To: \n" 16 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 17 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 18 | "Last-Translator: Tomáš Hurta, 2021\n" 19 | "Language-Team: Czech (https://www.transifex.com/linuxdeepin/teams/3617/cs/)\n" 20 | "MIME-Version: 1.0\n" 21 | "Content-Type: text/plain; charset=UTF-8\n" 22 | "Content-Transfer-Encoding: 8bit\n" 23 | "Language: cs\n" 24 | "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" 25 | 26 | #: ../../main.go:410 ../../main.go:446 27 | #, c-format 28 | msgid "Your password will expire in %d days" 29 | msgstr "Platnost vašeho hesla skončí za %d dnů" 30 | 31 | #: ../../main.go:414 ../../main.go:448 32 | #, c-format 33 | msgid "%d login failures since the last successful login" 34 | msgstr "%d neúspěšných přihlášení od posledního úspěšného přihlášení" 35 | 36 | #: ../../main.go:423 37 | msgid "Login Reminder" 38 | msgstr "Připomínka přihlášení" 39 | 40 | #: ../../main.go:424 41 | msgid "Details" 42 | msgstr "Podrobnosti" 43 | 44 | #: ../../main.go:444 45 | #, c-format 46 | msgid "Login time: %s" 47 | msgstr "Čas přihlášení: %s" 48 | 49 | #: ../../main.go:445 50 | #, c-format 51 | msgid "Last login: %s" 52 | msgstr "Naposledy přihlášen: %s" 53 | -------------------------------------------------------------------------------- /misc/po/sr.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Nikola Obradović , 2021 8 | # Марко Пејовић , 2021 9 | # 10 | #, fuzzy 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: PACKAGE VERSION\n" 14 | "Report-Msgid-Bugs-To: \n" 15 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 16 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 17 | "Last-Translator: Марко Пејовић , 2021\n" 18 | "Language-Team: Serbian (https://www.transifex.com/linuxdeepin/teams/3617/sr/)\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Language: sr\n" 23 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 24 | 25 | #: ../../main.go:410 ../../main.go:446 26 | #, c-format 27 | msgid "Your password will expire in %d days" 28 | msgstr "Лозинка истиче за %d дан(а)" 29 | 30 | #: ../../main.go:414 ../../main.go:448 31 | #, c-format 32 | msgid "%d login failures since the last successful login" 33 | msgstr "%d неуспрешних пријављивања од прошле успешне пријаве" 34 | 35 | #: ../../main.go:423 36 | msgid "Login Reminder" 37 | msgstr "Подсетник пријављивања" 38 | 39 | #: ../../main.go:424 40 | msgid "Details" 41 | msgstr "Детаљи" 42 | 43 | #: ../../main.go:444 44 | #, c-format 45 | msgid "Login time: %s" 46 | msgstr "Време пријављивања: %s" 47 | 48 | #: ../../main.go:445 49 | #, c-format 50 | msgid "Last login: %s" 51 | msgstr "Последње пријављивање: %s" 52 | -------------------------------------------------------------------------------- /misc/po/es.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Pablo Alejandro Hamann , 2021 8 | # Isaías Gätjens M , 2021 9 | # Alvaro Samudio , 2021 10 | # 11 | #, fuzzy 12 | msgid "" 13 | msgstr "" 14 | "Project-Id-Version: PACKAGE VERSION\n" 15 | "Report-Msgid-Bugs-To: \n" 16 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 17 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 18 | "Last-Translator: Alvaro Samudio , 2021\n" 19 | "Language-Team: Spanish (https://www.transifex.com/linuxdeepin/teams/3617/es/)\n" 20 | "MIME-Version: 1.0\n" 21 | "Content-Type: text/plain; charset=UTF-8\n" 22 | "Content-Transfer-Encoding: 8bit\n" 23 | "Language: es\n" 24 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 25 | 26 | #: ../../main.go:410 ../../main.go:446 27 | #, c-format 28 | msgid "Your password will expire in %d days" 29 | msgstr "La contraseña caduracá en %d días" 30 | 31 | #: ../../main.go:414 ../../main.go:448 32 | #, c-format 33 | msgid "%d login failures since the last successful login" 34 | msgstr "" 35 | " %d fallos de inicio de sesión desde el último inicio de sesión con éxito" 36 | 37 | #: ../../main.go:423 38 | msgid "Login Reminder" 39 | msgstr "Recordatorio de inicio de sesión" 40 | 41 | #: ../../main.go:424 42 | msgid "Details" 43 | msgstr "Detalles" 44 | 45 | #: ../../main.go:444 46 | #, c-format 47 | msgid "Login time: %s" 48 | msgstr "Hora de inicio de sesión: %s" 49 | 50 | #: ../../main.go:445 51 | #, c-format 52 | msgid "Last login: %s" 53 | msgstr "Último inicio de sesión: %s" 54 | -------------------------------------------------------------------------------- /misc/po/pl.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Hubert Ożga , 2021 8 | # Piotr Strębski , 2021 9 | # Janusz Ruchała, 2021 10 | # 11 | #, fuzzy 12 | msgid "" 13 | msgstr "" 14 | "Project-Id-Version: PACKAGE VERSION\n" 15 | "Report-Msgid-Bugs-To: \n" 16 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 17 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 18 | "Last-Translator: Janusz Ruchała, 2021\n" 19 | "Language-Team: Polish (https://www.transifex.com/linuxdeepin/teams/3617/pl/)\n" 20 | "MIME-Version: 1.0\n" 21 | "Content-Type: text/plain; charset=UTF-8\n" 22 | "Content-Transfer-Encoding: 8bit\n" 23 | "Language: pl\n" 24 | "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" 25 | 26 | #: ../../main.go:410 ../../main.go:446 27 | #, c-format 28 | msgid "Your password will expire in %d days" 29 | msgstr "Twoje hasło wygaśnie za 1%d dni" 30 | 31 | #: ../../main.go:414 ../../main.go:448 32 | #, c-format 33 | msgid "%d login failures since the last successful login" 34 | msgstr "%d nieudanych logowań od ostatniego udanego" 35 | 36 | #: ../../main.go:423 37 | msgid "Login Reminder" 38 | msgstr "Przypomnienie danych logowania" 39 | 40 | #: ../../main.go:424 41 | msgid "Details" 42 | msgstr "Szczegóły" 43 | 44 | #: ../../main.go:444 45 | #, c-format 46 | msgid "Login time: %s" 47 | msgstr "Czas logowania: %s" 48 | 49 | #: ../../main.go:445 50 | #, c-format 51 | msgid "Last login: %s" 52 | msgstr "Ostatnie logowanie: %s" 53 | -------------------------------------------------------------------------------- /misc/po/uk.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | # Translators: 7 | # Yuri Chornoivan , 2021 8 | # 9 | #, fuzzy 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: PACKAGE VERSION\n" 13 | "Report-Msgid-Bugs-To: \n" 14 | "POT-Creation-Date: 2021-09-28 10:19+0800\n" 15 | "PO-Revision-Date: 2021-08-31 07:06+0000\n" 16 | "Last-Translator: Yuri Chornoivan , 2021\n" 17 | "Language-Team: Ukrainian (https://www.transifex.com/linuxdeepin/teams/3617/uk/)\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Language: uk\n" 22 | "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" 23 | 24 | #: ../../main.go:410 ../../main.go:446 25 | #, c-format 26 | msgid "Your password will expire in %d days" 27 | msgstr "Строк дії вашого пароля буде вичерпано за %d днів" 28 | 29 | #: ../../main.go:414 ../../main.go:448 30 | #, c-format 31 | msgid "%d login failures since the last successful login" 32 | msgstr "%d невдалих спроб увійти з часу останнього успішного входу" 33 | 34 | #: ../../main.go:423 35 | msgid "Login Reminder" 36 | msgstr "Нагадування щодо входу" 37 | 38 | #: ../../main.go:424 39 | msgid "Details" 40 | msgstr "Подробиці" 41 | 42 | #: ../../main.go:444 43 | #, c-format 44 | msgid "Login time: %s" 45 | msgstr "Час входу: %s" 46 | 47 | #: ../../main.go:445 48 | #, c-format 49 | msgid "Last login: %s" 50 | msgstr "Останній вхід: %s" 51 | -------------------------------------------------------------------------------- /xsettings/exported_methods_auto.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | // Code generated by "dbusutil-gen em -type XSManager"; DO NOT EDIT. 6 | 7 | package xsettings 8 | 9 | import ( 10 | "github.com/linuxdeepin/go-lib/dbusutil" 11 | ) 12 | 13 | func (v *XSManager) GetExportedMethods() dbusutil.ExportedMethods { 14 | return dbusutil.ExportedMethods{ 15 | { 16 | Name: "GetColor", 17 | Fn: v.GetColor, 18 | InArgs: []string{"prop"}, 19 | OutArgs: []string{"outArg0"}, 20 | }, 21 | { 22 | Name: "GetInteger", 23 | Fn: v.GetInteger, 24 | InArgs: []string{"prop"}, 25 | OutArgs: []string{"outArg0"}, 26 | }, 27 | { 28 | Name: "GetScaleFactor", 29 | Fn: v.GetScaleFactor, 30 | OutArgs: []string{"outArg0"}, 31 | }, 32 | { 33 | Name: "GetScreenScaleFactors", 34 | Fn: v.GetScreenScaleFactors, 35 | OutArgs: []string{"outArg0"}, 36 | }, 37 | { 38 | Name: "GetString", 39 | Fn: v.GetString, 40 | InArgs: []string{"prop"}, 41 | OutArgs: []string{"outArg0"}, 42 | }, 43 | { 44 | Name: "ListProps", 45 | Fn: v.ListProps, 46 | OutArgs: []string{"outArg0"}, 47 | }, 48 | { 49 | Name: "SetColor", 50 | Fn: v.SetColor, 51 | InArgs: []string{"prop", "v"}, 52 | }, 53 | { 54 | Name: "SetInteger", 55 | Fn: v.SetInteger, 56 | InArgs: []string{"prop", "v"}, 57 | }, 58 | { 59 | Name: "SetScaleFactor", 60 | Fn: v.SetScaleFactor, 61 | InArgs: []string{"scale"}, 62 | }, 63 | { 64 | Name: "SetScreenScaleFactors", 65 | Fn: v.SetScreenScaleFactors, 66 | InArgs: []string{"factors"}, 67 | }, 68 | { 69 | Name: "SetString", 70 | Fn: v.SetString, 71 | InArgs: []string{"prop", "v"}, 72 | }, 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: startdde 3 | Upstream-Contact: UnionTech Software Technology Co., Ltd. <> 4 | Source: https://github.com/linuxdeepin/startdde 5 | 6 | # gitignore clang-format gitreview .tx 7 | Files: .gitignore .clang-format .gitreview .mailmap .tx/config 8 | Copyright: None 9 | License: CC0-1.0 10 | 11 | # ci 12 | Files: .github/* .obs/* .gitlab-ci.yml 13 | Copyright: None 14 | License: CC0-1.0 15 | 16 | # debian rpm archlinux 17 | Files: debian/* rpm/* archlinux/* 18 | Copyright: None 19 | License: CC0-1.0 20 | 21 | # README 22 | Files: *.md graph/README README.zh_CN.md 23 | Copyright: UnionTech Software Technology Co., Ltd. 24 | License: CC-BY-4.0 25 | 26 | # org xml json yaml html yml css 27 | Files: *.org *.xml *.json *.yaml *.html *.yml *.css *.devhelp2 28 | Copyright: UnionTech Software Technology Co., Ltd. 29 | License: GPL-3.0-or-later 30 | 31 | # docs 32 | Files: docs/* 33 | Copyright: UnionTech Software Technology Co., Ltd. 34 | License: GPL-3.0-or-later 35 | 36 | # Project file 37 | Files: *Makefile* 38 | Copyright: None 39 | License: CC0-1.0 40 | 41 | # sh 42 | Files: gen.sh _tool/prop_gen/gen.sh gen_test_report.sh 43 | Copyright: None 44 | License: CC0-1.0 45 | 46 | # translation or configuration files 47 | Files: misc/* 48 | Copyright: UnionTech Software Technology Co., Ltd. 49 | License: GPL-3.0-or-later 50 | 51 | # png svg 52 | Files: *.png *.svg *.jpg 53 | Copyright: UnionTech Software Technology Co., Ltd. 54 | License: GPL-3.0-or-later 55 | 56 | # configuration file 57 | Files: *.conf *.gir 58 | Copyright: UnionTech Software Technology Co., Ltd. 59 | License: GPL-3.0-or-later 60 | 61 | # test file or examples 62 | Files: *testdata/* *examples/* 63 | Copyright: UnionTech Software Technology Co., Ltd. 64 | License: GPL-3.0-or-later 65 | 66 | # auto generation files 67 | Files: *_methods_auto.go *_dbusutil.go *_gen.go 68 | Copyright: UnionTech Software Technology Co., Ltd. 69 | License: GPL-3.0-or-later 70 | 71 | # gomod files 72 | Files: go.mod go.sum 73 | Copyright: None 74 | License: CC0-1.0 75 | -------------------------------------------------------------------------------- /xsettings/utils_gsettings.go: -------------------------------------------------------------------------------- 1 | package xsettings 2 | 3 | import ( 4 | "github.com/linuxdeepin/go-gir/gio-2.0" 5 | "github.com/linuxdeepin/go-lib/gsettings" 6 | "github.com/linuxdeepin/go-lib/strv" 7 | ) 8 | 9 | type GSConfig struct { 10 | gs *gio.Settings 11 | keyList []string 12 | } 13 | 14 | func NewGSConfig() *GSConfig { 15 | gs := gio.NewSettings(xsSchema) 16 | keyList := gs.ListKeys() 17 | return &GSConfig{ 18 | gs: gs, 19 | keyList: keyList, 20 | } 21 | } 22 | 23 | func (gc *GSConfig) ListKeys() []string { 24 | return gc.keyList 25 | } 26 | 27 | func (gc *GSConfig) hasKey(key string) bool { 28 | if !strv.Strv(gc.keyList).Contains(key) { 29 | logger.Warning("key %v not found in dconfig", key) 30 | return false 31 | } 32 | return true 33 | } 34 | 35 | func (gc *GSConfig) GetString(key string) string { 36 | if !gc.hasKey(key) { 37 | return "" 38 | } 39 | return gc.gs.GetString(key) 40 | } 41 | 42 | func (gc *GSConfig) GetInt(key string) int32 { 43 | if !gc.hasKey(key) { 44 | return -1 45 | } 46 | return gc.gs.GetInt(key) 47 | } 48 | 49 | func (gc *GSConfig) GetBoolean(key string) bool { 50 | if !gc.hasKey(key) { 51 | return false 52 | } 53 | return gc.gs.GetBoolean(key) 54 | } 55 | 56 | func (gc *GSConfig) GetDouble(key string) float64 { 57 | if !gc.hasKey(key) { 58 | return -1 59 | } 60 | return gc.gs.GetDouble(key) 61 | } 62 | 63 | func (gc *GSConfig) SetString(key string, value string) bool { 64 | if !gc.hasKey(key) { 65 | return false 66 | } 67 | return gc.gs.SetString(key, value) 68 | } 69 | 70 | func (gc *GSConfig) SetInt(key string, value int32) bool { 71 | if !gc.hasKey(key) { 72 | return false 73 | } 74 | return gc.gs.SetInt(key, value) 75 | } 76 | 77 | func (gc *GSConfig) SetBoolean(key string, value bool) bool { 78 | if !gc.hasKey(key) { 79 | return false 80 | } 81 | return gc.gs.SetBoolean(key, value) 82 | } 83 | 84 | func (gc *GSConfig) SetDouble(key string, value float64) bool { 85 | if !gc.hasKey(key) { 86 | return false 87 | } 88 | return gc.gs.SetDouble(key, value) 89 | } 90 | 91 | func (d *GSConfig) HandleConfigChanged(cb func(string)) { 92 | gsettings.ConnectChanged(xsSchema, "*", cb) 93 | } 94 | -------------------------------------------------------------------------------- /README.zh_CN.md: -------------------------------------------------------------------------------- 1 | # Startdde 2 | 3 | **描述**: 4 | startdde 用于启动DDE组件和调用 5 | 符合xdg自动启动规范的用户自定义应用程序。 6 | 7 | ## 依赖 8 | 9 | ### 编译依赖 10 | 11 | - cmake 12 | - pkg-config 13 | - golang-go 14 | - [go-dlib](https://github.com/linuxdeepin/go-lib) 15 | - [go-fsnotify](https://github.com/howeyc/fsnotify) 16 | - [dde-dbus-factory](https://github.com/linuxdeepin/dbus-factory) 17 | - [go-gir-generator](https://github.com/linuxdeepin/go-gir-generator) 18 | - [dde-api](https://github.com/linuxdeepin/dde-api) 19 | - [go-x11-client](https://github.com/linuxdeepin/go-x11-client) 20 | - libgnome-keyring 21 | - libxfixes 22 | - libxcursor 23 | 24 | ### 运行依赖 25 | 26 | - dde-daemon 27 | - deepin-wm | deepin-metacity 28 | - libgnome-keyring 29 | - libxfixes 30 | - libxcursor 31 | 32 | ## 安装 33 | 34 | ### Deepin 35 | 36 | startdde需要预安装以下包 37 | ``` 38 | $ sudo apt-get build-dep startdde 39 | ``` 40 | 41 | 构建 42 | ``` 43 | $ GOPATH=/usr/share/gocode make 44 | ``` 45 | 46 | 如果你有独立的测试构建环境(比如一个 docker 容器),你可以直接安装它 47 | ``` 48 | $ sudo make install 49 | ``` 50 | 51 | 生成包文件并使用它安装startdde 52 | ``` 53 | $ debuild -uc -us ... 54 | $ sudo dpkg -i ../startdde-*deb 55 | ``` 56 | 57 | ## 使用方法 58 | 59 | 使用以下命令运行 Startdde 60 | 61 | ``` 62 | Usage of /usr/bin/startdde: 63 | -d=false: debug 64 | ``` 65 | 66 | 67 | ### 无需显示管理器直接运行 68 | 69 | ``` 70 | $ echo "dbus-launch --exit-with-session /usr/bin/startdde" > ~/.xinitrc 71 | $ startx 72 | ``` 73 | 74 | ### 使用显示管理器运行 75 | 76 | 1. 在`/usr/share/xsessions`中构建会话桌面 77 | 78 | ``` 79 | cat /usr/share/xsessions/deepin.desktop 80 | 81 | [Desktop Entry] 82 | Name=Deepin 83 | Comment=Deepin Desktop Environment 84 | Exec=/usr/bin/startdde 85 | ``` 86 | 87 | 2. 使用相应的显示管理器如gdm, kdm或者lightdm来启动startdde 88 | 89 | ## 获得帮助 90 | 91 | 如果您遇到任何其他问题,您可能还会发现这些渠道很有用: 92 | 93 | * [Gitter](https://gitter.im/orgs/linuxdeepin/rooms) 94 | * [IRC channel](https://webchat.freenode.net/?channels=deepin) 95 | * [Forum](https://bbs.deepin.org) 96 | * [WiKi](https://wiki.deepin.org/) 97 | 98 | ## 贡献指南 99 | 100 | 我们鼓励您报告问题并做出更改。 101 | 102 | * [Contribution guide for developers](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers-en). (English) 103 | * [开发者代码贡献指南](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers) (中文) 104 | 105 | ## 开源协议 106 | 107 | startdde项目在 [GPL-3.0-or-later](LICENSE)下发布。 108 | -------------------------------------------------------------------------------- /display/monitor_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/linuxdeepin/go-lib/dbusutil" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func Test_getFirstModeBySize(t *testing.T) { 15 | modes := []ModeInfo{{84, "", 1920, 1080, 60.0}, {85, "", 1920, 1080, 50.0}, 16 | {95, "", 1600, 1200, 60.0}} 17 | assert.Equal(t, getFirstModeBySize(modes, 1920, 1080), ModeInfo{84, "", 1920, 1080, 60.0}) 18 | assert.Equal(t, getFirstModeBySize(modes, 1600, 1200), ModeInfo{95, "", 1600, 1200, 60.0}) 19 | assert.Equal(t, getFirstModeBySize(modes, 1280, 740), ModeInfo{}) 20 | } 21 | 22 | func Test_getFirstModeBySizeRate(t *testing.T) { 23 | modes := []ModeInfo{{84, "", 1920, 1080, 60.0}, {85, "", 1920, 1080, 50.0}, 24 | {95, "", 1600, 1200, 60.0}} 25 | assert.Equal(t, getFirstModeBySizeRate(modes, 1920, 1080, 59.99), ModeInfo{84, "", 1920, 1080, 60.0}) 26 | assert.Equal(t, getFirstModeBySizeRate(modes, 1920, 1080, 49.99), ModeInfo{85, "", 1920, 1080, 50.0}) 27 | assert.Equal(t, getFirstModeBySizeRate(modes, 1600, 1200, 59), ModeInfo{}) 28 | assert.Equal(t, getFirstModeBySizeRate(modes, 1280, 740, 60), ModeInfo{}) 29 | 30 | } 31 | 32 | func Test_getRandrStatusStr(t *testing.T) { 33 | var status = []uint8{0, 1, 2, 3, 4} 34 | var statusstr = []string{"success", "invalid config time", "invalid time", "failed", "unknown status 4"} 35 | assert.Equal(t, getRandrStatusStr(status[0]), statusstr[0]) 36 | assert.Equal(t, getRandrStatusStr(status[1]), statusstr[1]) 37 | assert.Equal(t, getRandrStatusStr(status[2]), statusstr[2]) 38 | assert.Equal(t, getRandrStatusStr(status[3]), statusstr[3]) 39 | assert.Equal(t, getRandrStatusStr(status[4]), statusstr[4]) 40 | } 41 | 42 | //func Test_setXrandrScalingMode(t *testing.T) { 43 | // var fillMode = "None" 44 | // m := Monitor{} 45 | // err := m.setScalingMode(fillMode) 46 | // assert.NotNil(t, err) 47 | //} 48 | 49 | func Test_generateFillModeKey(t *testing.T) { 50 | m := Monitor{} 51 | m.uuid = "VGA-16813e57c97781347115de0e64f8277ec" 52 | m.Height = 1080 53 | m.Width = 1920 54 | assert.Equal(t, "VGA-16813e57c97781347115de0e64f8277ec:1920x1080", 55 | m.generateFillModeKey()) 56 | 57 | m.Rotation = 2 58 | assert.Equal(t, "VGA-16813e57c97781347115de0e64f8277ec:1080x1920", 59 | m.generateFillModeKey()) 60 | } 61 | 62 | func Test_setCurrentFillMode(t *testing.T) { 63 | m := Monitor{} 64 | write := &dbusutil.PropertyWrite{ 65 | Value: "None", 66 | } 67 | 68 | m.m = &Manager{} 69 | err := m.setCurrentFillMode(write) 70 | assert.NotNil(t, err) 71 | } 72 | -------------------------------------------------------------------------------- /cmd/fix-xauthority-perm/main.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package main 6 | 7 | import ( 8 | "errors" 9 | "log" 10 | "os" 11 | "path/filepath" 12 | "strconv" 13 | "syscall" 14 | 15 | "github.com/godbus/dbus/v5" 16 | accounts "github.com/linuxdeepin/go-dbus-factory/system/org.deepin.dde.accounts1" 17 | ) 18 | 19 | func init() { 20 | log.SetFlags(log.Lshortfile) 21 | } 22 | 23 | const ( 24 | stdXAuthFileMod = 0600 25 | ) 26 | 27 | func fix(conn *dbus.Conn, userPath string) error { 28 | userObj, err := accounts.NewUser(conn, dbus.ObjectPath(userPath)) 29 | if err != nil { 30 | return err 31 | } 32 | homeDir, err := userObj.HomeDir().Get(0) 33 | if err != nil { 34 | return err 35 | } 36 | uidStr, err := userObj.Uid().Get(0) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | uid, err := strconv.Atoi(uidStr) 42 | if err != nil { 43 | return err 44 | } 45 | xAuthFile := os.Getenv("XAUTHORITY") 46 | if len(xAuthFile) == 0 { 47 | xAuthFile = filepath.Join(homeDir, ".Xauthority") 48 | } 49 | fileInfo, err := os.Stat(xAuthFile) 50 | if err != nil { 51 | if os.IsNotExist(err) { 52 | return createXAuthFile(xAuthFile, uid) 53 | } 54 | return err 55 | } 56 | 57 | // fix mod 58 | if fileInfo.Mode() != stdXAuthFileMod { 59 | err = os.Chmod(xAuthFile, stdXAuthFileMod) 60 | if err != nil { 61 | return err 62 | } 63 | } 64 | 65 | sysStat, ok := fileInfo.Sys().(*syscall.Stat_t) 66 | if !ok { 67 | return errors.New("failed to convert fileInfo.Sys() to *syscall.Stat_t") 68 | } 69 | 70 | // fix own 71 | if int(sysStat.Uid) != uid { 72 | err = os.Chown(xAuthFile, uid, uid) 73 | if err != nil { 74 | return err 75 | } 76 | } 77 | 78 | return nil 79 | } 80 | 81 | func createXAuthFile(filename string, uid int) error { 82 | fh, err := os.Create(filename) 83 | if err != nil { 84 | return err 85 | } 86 | defer fh.Close() 87 | 88 | _, err = fh.Write([]byte{0}) 89 | if err != nil { 90 | return err 91 | } 92 | 93 | err = fh.Chmod(stdXAuthFileMod) 94 | if err != nil { 95 | return err 96 | } 97 | 98 | err = fh.Chown(uid, uid) 99 | if err != nil { 100 | return err 101 | } 102 | 103 | return nil 104 | } 105 | 106 | func main() { 107 | sysBus, err := dbus.SystemBus() 108 | if err != nil { 109 | log.Fatal(err) 110 | } 111 | accountsObj := accounts.NewAccounts(sysBus) 112 | userList, err := accountsObj.UserList().Get(0) 113 | if err != nil { 114 | log.Fatal(err) 115 | } 116 | for _, userPath := range userList { 117 | err := fix(sysBus, userPath) 118 | if err != nil { 119 | log.Println(err) 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Startdde 2 | 3 | **Description**: 4 | Startdde is used for launching DDE components and invoking 5 | user's custom applications which compliant with xdg autostart specification. 6 | 7 | ## Dependencies 8 | 9 | ### Build dependencies 10 | 11 | - cmake 12 | - pkg-config 13 | - golang-go 14 | - [go-dlib](https://github.com/linuxdeepin/go-lib) 15 | - [go-fsnotify](https://github.com/howeyc/fsnotify) 16 | - [dde-dbus-factory](https://github.com/linuxdeepin/dbus-factory) 17 | - [go-gir-generator](https://github.com/linuxdeepin/go-gir-generator) 18 | - [dde-api](https://github.com/linuxdeepin/dde-api) 19 | - [go-x11-client](https://github.com/linuxdeepin/go-x11-client) 20 | - libgnome-keyring 21 | - libxfixes 22 | - libxcursor 23 | 24 | ### Runtime dependencies 25 | 26 | - dde-daemon 27 | - deepin-wm | deepin-metacity 28 | - libgnome-keyring 29 | - libxfixes 30 | - libxcursor 31 | 32 | ## Installation 33 | 34 | ### Deepin 35 | 36 | Install prerequisites 37 | ``` 38 | $ sudo apt-get build-dep startdde 39 | ``` 40 | 41 | Build 42 | ``` 43 | $ GOPATH=/usr/share/gocode make 44 | ``` 45 | 46 | If you have isolated testing build environment (say a docker container), you can install it directly 47 | ``` 48 | $ sudo make install 49 | ``` 50 | 51 | generate package files and install Startdde with it 52 | ``` 53 | $ debuild -uc -us ... 54 | $ sudo dpkg -i ../startdde-*deb 55 | ``` 56 | 57 | ## Usage 58 | 59 | Run Startdde with the command below. 60 | 61 | ``` 62 | Usage of /usr/bin/startdde: 63 | -d=false: debug 64 | ``` 65 | 66 | 67 | ### Directly run without display manager 68 | 69 | ``` 70 | $ echo "dbus-launch --exit-with-session /usr/bin/startdde" > ~/.xinitrc 71 | $ startx 72 | ``` 73 | 74 | ### Run with display manager 75 | 76 | 1. construct a session desktop in /usr/share/xsessions 77 | 78 | ``` 79 | cat /usr/share/xsessions/deepin.desktop 80 | 81 | [Desktop Entry] 82 | Name=Deepin 83 | Comment=Deepin Desktop Environment 84 | Exec=/usr/bin/startdde 85 | ``` 86 | 87 | 2. Using DisplayManager like, gdm, kdm or lightdm to startup Startdde 88 | 89 | ## Getting help 90 | 91 | Any usage issues can ask for help via 92 | 93 | * [Gitter](https://gitter.im/orgs/linuxdeepin/rooms) 94 | * [IRC channel](https://webchat.freenode.net/?channels=deepin) 95 | * [Forum](https://bbs.deepin.org) 96 | * [WiKi](https://wiki.deepin.org/) 97 | 98 | ## Getting involved 99 | 100 | We encourage you to report issues and contribute changes 101 | 102 | * [Contribution guide for developers](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers-en). (English) 103 | * [开发者代码贡献指南](https://github.com/linuxdeepin/developer-center/wiki/Contribution-Guidelines-for-Developers) (中文) 104 | 105 | ## License 106 | 107 | Startdde is licensed under [GPL-3.0-or-later](LICENSE). 108 | -------------------------------------------------------------------------------- /rpm/startdde.spec: -------------------------------------------------------------------------------- 1 | %global _missing_build_ids_terminate_build 0 2 | %global debug_package %{nil} 3 | 4 | %define specrelease 2%{?dist} 5 | %if 0%{?openeuler} 6 | %define specrelease 2 7 | %endif 8 | 9 | Name: startdde 10 | Version: 5.6.0.7 11 | Release: %{specrelease} 12 | Summary: Starter of deepin desktop environment 13 | License: GPLv3 14 | URL: https://github.com/linuxdeepin/startdde 15 | Source0: %{name}-%{version}.tar.xz 16 | 17 | BuildRequires: golang 18 | BuildRequires: jq 19 | BuildRequires: gocode 20 | BuildRequires: glib2-devel 21 | BuildRequires: pkgconfig(x11) 22 | BuildRequires: libXcursor-devel 23 | BuildRequires: libXfixes-devel 24 | BuildRequires: gtk3-devel 25 | BuildRequires: pulseaudio-libs-devel 26 | BuildRequires: libgnome-keyring-devel 27 | BuildRequires: alsa-lib-devel 28 | BuildRequires: pkgconfig(gudev-1.0) 29 | 30 | Provides: x-session-manager 31 | Requires: dde-daemon 32 | Requires: procps 33 | Requires: gocode 34 | Requires: deepin-desktop-schemas 35 | Requires: libXfixes 36 | Requires: libXcursor 37 | Recommends: dde-qt5integration 38 | 39 | %description 40 | %{summary}. 41 | 42 | %prep 43 | %autosetup -n %{name}-%{version} 44 | patch Makefile < rpm/Makefile.patch 45 | patch misc/auto_launch/chinese.json < rpm/chinese.json.patch 46 | patch misc/auto_launch/default.json < rpm/default.json.patch 47 | patch main.go < rpm/main.go.patch 48 | 49 | %build 50 | export GOPATH=/usr/share/gocode 51 | 52 | ## Scripts in /etc/X11/Xsession.d are not executed after xorg start 53 | sed -i 's|X11/Xsession.d|X11/xinit/xinitrc.d|g' Makefile 54 | 55 | %make_build GO_BUILD_FLAGS=-trimpath 56 | 57 | %install 58 | %make_install 59 | 60 | %post 61 | xsOptsFile=/etc/X11/Xsession.options 62 | update-alternatives --install /usr/bin/x-session-manager x-session-manager \ 63 | /usr/bin/startdde 90 || true 64 | if [ -f $xsOptsFile ];then 65 | sed -i '/^use-ssh-agent/d' $xsOptsFile 66 | if ! grep '^no-use-ssh-agent' $xsOptsFile >/dev/null; then 67 | echo no-use-ssh-agent >> $xsOptsFile 68 | fi 69 | fi 70 | 71 | %files 72 | %doc README.md 73 | %license LICENSE 74 | %{_sysconfdir}/X11/xinit/xinitrc.d/00deepin-dde-env 75 | %{_sysconfdir}/X11/xinit/xinitrc.d/01deepin-profile 76 | %{_sysconfdir}/profile.d/deepin-xdg-dir.sh 77 | %{_bindir}/%{name} 78 | %{_sbindir}/deepin-fix-xauthority-perm 79 | %{_datadir}/xsessions/deepin.desktop 80 | %{_datadir}/lightdm/lightdm.conf.d/60-deepin.conf 81 | %{_datadir}/%{name}/auto_launch.json 82 | %{_datadir}/%{name}/memchecker.json 83 | /usr/lib/deepin-daemon/greeter-display-daemon 84 | 85 | %changelog 86 | * Wed Oct 14 2020 guoqinglan - 5.6.0.5-2 87 | - bugfix-49318, modify /usr/lib/deepin-daemon path 88 | 89 | * Sat Oct 10 2020 guoqinglan - 5.6.0.5-1 90 | - bugfix-49970, fix post add preun scripts 91 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX = /usr 2 | GOPATH_DIR = gopath 3 | GOPKG_PREFIX = github.com/linuxdeepin/startdde 4 | GOBUILD = go build -v $(GO_BUILD_FLAGS) 5 | export GOPATH= $(shell go env GOPATH) 6 | 7 | LANGUAGES = $(basename $(notdir $(wildcard misc/po/*.po))) 8 | 9 | all: build 10 | 11 | prepare: 12 | @mkdir -p ${GOPATH_DIR}/src/$(dir ${GOPKG_PREFIX}); 13 | @ln -snf ../../../.. ${GOPATH_DIR}/src/${GOPKG_PREFIX}; 14 | 15 | startdde: 16 | env GOPATH="${CURDIR}/${GOPATH_DIR}:${GOPATH}" ${GOBUILD} -o startdde ${GOPKG_PREFIX} 17 | 18 | fix-xauthority-perm: 19 | env GOPATH="${CURDIR}/${GOPATH_DIR}:${GOPATH}" ${GOBUILD} -o fix-xauthority-perm ${GOPKG_PREFIX}/cmd/fix-xauthority-perm 20 | 21 | out/locale/%/LC_MESSAGES/startdde.mo: misc/po/%.po 22 | mkdir -p $(@D) 23 | msgfmt -o $@ $< 24 | 25 | translate: $(addsuffix /LC_MESSAGES/startdde.mo, $(addprefix out/locale/, ${LANGUAGES})) 26 | 27 | pot: 28 | deepin-update-pot misc/po/locale_config.ini 29 | 30 | build: prepare startdde fix-xauthority-perm translate 31 | 32 | test: prepare 33 | env GOPATH="${CURDIR}/${GOPATH_DIR}:${GOPATH}" go test -v ${GOPKG_PREFIX} 34 | 35 | test-coverage: prepare 36 | env GOPATH="${CURDIR}/${GOPATH_DIR}:${GOPATH}" go test -cover -v ${GOPKG_PREFIX} | awk '$$1 ~ "(ok|\\?)" {print $$2","$$5}' | sed "s:${CURDIR}::g" | sed 's/files\]/0\.0%/g' > coverage.csv 37 | 38 | print_gopath: prepare 39 | GOPATH="${CURDIR}/${GOPATH_DIR}:${GOPATH}" 40 | 41 | install: 42 | install -Dm755 startdde ${DESTDIR}${PREFIX}/bin/startdde 43 | install -Dm755 fix-xauthority-perm ${DESTDIR}${PREFIX}/sbin/deepin-fix-xauthority-perm 44 | install -d -m755 ${DESTDIR}${PREFIX}/lib/deepin-daemon/ 45 | ln -sfv ../../bin/startdde ${DESTDIR}${PREFIX}/lib/deepin-daemon/greeter-display-daemon 46 | install -Dm644 misc/lightdm.conf ${DESTDIR}${PREFIX}/share/lightdm/lightdm.conf.d/60-deepin.conf 47 | mkdir -p ${DESTDIR}${PREFIX}/share/startdde/ 48 | install -v -m0644 misc/filter.conf ${DESTDIR}${PREFIX}/share/startdde/ 49 | mkdir -p $(DESTDIR)$(PREFIX)/share/glib-2.0/schemas 50 | install -v -m0644 misc/schemas/*.xml $(DESTDIR)$(PREFIX)/share/glib-2.0/schemas/ 51 | 52 | mkdir -p $(DESTDIR)$(PREFIX)/share/dsg/configs/org.deepin.startdde/ 53 | install -v -m0644 misc/dsettings/*.json $(DESTDIR)$(PREFIX)/share/dsg/configs/org.deepin.startdde/ 54 | 55 | mkdir -pv ${DESTDIR}${PREFIX}/share/locale 56 | cp -r out/locale/* ${DESTDIR}${PREFIX}/share/locale 57 | 58 | mkdir -p $(DESTDIR)$(PREFIX)/lib/systemd/user/dde-session-initialized.target.wants/ 59 | install -v -m0644 misc/systemd_task/dde-display-task-refresh-brightness.service $(DESTDIR)$(PREFIX)/lib/systemd/user/ 60 | ln -s $(PREFIX)/lib/systemd/user/dde-display-task-refresh-brightness.service $(DESTDIR)$(PREFIX)/lib/systemd/user/dde-session-initialized.target.wants/dde-display-task-refresh-brightness.service 61 | 62 | 63 | clean: 64 | rm -rf ${GOPATH_DIR} 65 | rm -f startdde 66 | rm -f fix-xauthority-perm 67 | 68 | rebuild: clean build 69 | 70 | check_code_quality: prepare 71 | env GOPATH="${CURDIR}/${GOPATH_DIR}:${GOPATH}" go vet ${GOPKG_PREFIX} 72 | 73 | .PHONY: startdde 74 | -------------------------------------------------------------------------------- /xsettings/xsettings_setting.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package xsettings 6 | 7 | import ( 8 | "os" 9 | 10 | "github.com/linuxdeepin/go-x11-client" 11 | "github.com/linuxdeepin/go-x11-client/util/wm/ewmh" 12 | ) 13 | 14 | const ( 15 | settingPropScreen = "_XSETTINGS_S0" 16 | settingPropSettings = "_XSETTINGS_SETTINGS" 17 | 18 | xsDataOrder = 0 19 | xsDataSerial = 0 20 | xsDataFormat = 8 21 | ) 22 | 23 | func getSelectionOwner(prop string, conn *x.Conn) (x.Window, error) { 24 | atom, err := getAtomByProp(prop, conn) 25 | if err != nil { 26 | return 0, err 27 | } 28 | 29 | reply, err := x.GetSelectionOwner(conn, atom).Reply(conn) 30 | if err != nil { 31 | return 0, err 32 | } 33 | 34 | return reply.Owner, nil 35 | } 36 | 37 | func isSelectionOwned(prop string, wid x.Window, conn *x.Conn) bool { 38 | owner, err := getSelectionOwner(prop, conn) 39 | if err != nil { 40 | return false 41 | } 42 | 43 | if owner == 0 || owner != wid { 44 | return false 45 | } 46 | 47 | return true 48 | } 49 | 50 | func getAtomByProp(prop string, conn *x.Conn) (x.Atom, error) { 51 | return conn.GetAtom(prop) 52 | } 53 | 54 | func getSettingPropValue(owner x.Window, conn *x.Conn) ([]byte, error) { 55 | atom, err := getAtomByProp(settingPropSettings, conn) 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | reply, err := x.GetProperty(conn, false, owner, 61 | atom, atom, 0, 10240).Reply(conn) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | return reply.Value, nil 67 | } 68 | 69 | func changeSettingProp(owner x.Window, data []byte, conn *x.Conn) error { 70 | atom, err := getAtomByProp(settingPropSettings, conn) 71 | if err != nil { 72 | return err 73 | } 74 | 75 | return x.ChangePropertyChecked(conn, x.PropModeReplace, 76 | owner, atom, atom, 77 | xsDataFormat, data).Check(conn) 78 | } 79 | 80 | func createSettingWindow(conn *x.Conn) (x.Window, error) { 81 | screenAtom, err := getAtomByProp(settingPropScreen, conn) 82 | if err != nil { 83 | return 0, err 84 | } 85 | 86 | xid, err := conn.AllocID() 87 | if err != nil { 88 | return 0, err 89 | } 90 | wid := x.Window(xid) 91 | 92 | root := conn.GetDefaultScreen().Root 93 | err = x.CreateWindowChecked(conn, 0, wid, root, 94 | 0, 0, 1, 1, 0, 95 | x.WindowClassInputOnly, x.CopyFromParent, 96 | 0, nil).Check(conn) 97 | if err != nil { 98 | return 0, err 99 | } 100 | 101 | err = changeWindowPid(conn, wid) 102 | if err != nil { 103 | return 0, err 104 | } 105 | 106 | err = x.SetSelectionOwnerChecked(conn, wid, screenAtom, 107 | x.CurrentTime).Check(conn) 108 | if err != nil { 109 | return 0, err 110 | } 111 | 112 | return wid, nil 113 | } 114 | 115 | func changeWindowPid(conn *x.Conn, wid x.Window) error { 116 | pid := uint32(os.Getpid()) 117 | return ewmh.SetWMPidChecked(conn, wid, pid).Check(conn) 118 | } 119 | 120 | func pad(n int) int { 121 | return x.Pad(n) 122 | } 123 | -------------------------------------------------------------------------------- /display/touchscreen_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/linuxdeepin/go-x11-client/ext/randr" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func Test_genTransformationMatrixAux(t *testing.T) { 15 | type args struct { 16 | offsetX int16 17 | offsetY int16 18 | screenWidth uint16 19 | screenHeight uint16 20 | totalDisplayWidth uint16 21 | totalDisplayHeight uint16 22 | rotation uint16 23 | } 24 | tests := []struct { 25 | name string 26 | args args 27 | want TransformationMatrix 28 | }{ 29 | { 30 | name: "genTransformationMatrixAux single screen", 31 | args: args{ 32 | offsetX: 0, 33 | offsetY: 0, 34 | screenWidth: 1920, 35 | screenHeight: 1080, 36 | totalDisplayWidth: 1920, 37 | totalDisplayHeight: 1080, 38 | rotation: randr.RotationRotate0, 39 | }, 40 | want: TransformationMatrix{ 41 | 1, 0, 0, 42 | 0, 1, 0, 43 | 0, 0, 1, 44 | }, 45 | }, 46 | { 47 | name: "genTransformationMatrixAux single screen rotate 90", 48 | args: args{ 49 | offsetX: 0, 50 | offsetY: 0, 51 | screenWidth: 1920, 52 | screenHeight: 1080, 53 | totalDisplayWidth: 1920, 54 | totalDisplayHeight: 1080, 55 | rotation: randr.RotationRotate90, 56 | }, 57 | want: TransformationMatrix{ 58 | 0, -1, 1, 59 | 1, 0, 0, 60 | 0, 0, 1, 61 | }, 62 | }, 63 | { 64 | name: "genTransformationMatrixAux single screen rotate 90 reflect X", 65 | args: args{ 66 | offsetX: 0, 67 | offsetY: 0, 68 | screenWidth: 1920, 69 | screenHeight: 1080, 70 | totalDisplayWidth: 1920, 71 | totalDisplayHeight: 1080, 72 | rotation: randr.RotationRotate90 | randr.RotationReflectX, 73 | }, 74 | want: TransformationMatrix{ 75 | 0, 1, 0, 76 | 1, 0, 0, 77 | 0, 0, 1, 78 | }, 79 | }, 80 | { 81 | name: "genTransformationMatrixAux single screen rotate 90 reflect All", 82 | args: args{ 83 | offsetX: 0, 84 | offsetY: 0, 85 | screenWidth: 1920, 86 | screenHeight: 1080, 87 | totalDisplayWidth: 1920, 88 | totalDisplayHeight: 1080, 89 | rotation: randr.RotationRotate90 | randr.RotationReflectX | randr.RotationReflectY, 90 | }, 91 | want: TransformationMatrix{ 92 | 0, 1, 0, 93 | -1, 0, 1, 94 | 0, 0, 1, 95 | }, 96 | }, 97 | } 98 | for _, tt := range tests { 99 | t.Run(tt.name, func(t *testing.T) { 100 | got := genTransformationMatrixAux(tt.args.offsetX, tt.args.offsetY, 101 | tt.args.screenWidth, tt.args.screenHeight, 102 | tt.args.totalDisplayWidth, tt.args.totalDisplayHeight, 103 | tt.args.rotation) 104 | assert.Equal(t, tt.want, got) 105 | }) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /display/testdata/display_v4.json: -------------------------------------------------------------------------------- 1 | { 2 | "HDMI-1,eDP-12fd580d2dc41168dce2efb1bf19adb54": {}, 3 | "HDMI-179de84f651c9a0566d17a8a739359ea1,eDP-12fd580d2dc41168dce2efb1bf19adb54": { 4 | "Mirror": { 5 | "Monitors": [ 6 | { 7 | "UUID": "eDP-12fd580d2dc41168dce2efb1bf19adb54", 8 | "Name": "eDP-1", 9 | "Enabled": true, 10 | "X": 0, 11 | "Y": 0, 12 | "Width": 1920, 13 | "Height": 1080, 14 | "Rotation": 1, 15 | "Reflect": 0, 16 | "RefreshRate": 59.9987883403725, 17 | "Brightness": 1, 18 | "Primary": true 19 | }, 20 | { 21 | "UUID": "HDMI-179de84f651c9a0566d17a8a739359ea1", 22 | "Name": "HDMI-1", 23 | "Enabled": true, 24 | "X": 0, 25 | "Y": 0, 26 | "Width": 1920, 27 | "Height": 1080, 28 | "Rotation": 1, 29 | "Reflect": 0, 30 | "RefreshRate": 60, 31 | "Brightness": 1, 32 | "Primary": false 33 | } 34 | ], 35 | "ColorTemperatureMode": 0, 36 | "ColorTemperatureManual": 6500 37 | } 38 | }, 39 | "HDMI-1e50ab458de66b5fd180c446cd3377106,eDP-12fd580d2dc41168dce2efb1bf19adb54": { 40 | "Extend": { 41 | "Monitors": [ 42 | { 43 | "UUID": "eDP-12fd580d2dc41168dce2efb1bf19adb54", 44 | "Name": "eDP-1", 45 | "Enabled": true, 46 | "X": 0, 47 | "Y": 1080, 48 | "Width": 1920, 49 | "Height": 1080, 50 | "Rotation": 1, 51 | "Reflect": 0, 52 | "RefreshRate": 59.9987883403725, 53 | "Brightness": 0.23, 54 | "Primary": true 55 | }, 56 | { 57 | "UUID": "HDMI-1e50ab458de66b5fd180c446cd3377106", 58 | "Name": "HDMI-1", 59 | "Enabled": true, 60 | "X": 1920, 61 | "Y": 0, 62 | "Width": 4096, 63 | "Height": 2160, 64 | "Rotation": 1, 65 | "Reflect": 0, 66 | "RefreshRate": 24, 67 | "Brightness": 1, 68 | "Primary": false 69 | } 70 | ], 71 | "ColorTemperatureMode": 0, 72 | "ColorTemperatureManual": 9999 73 | } 74 | }, 75 | "eDP-12fd580d2dc41168dce2efb1bf19adb54": { 76 | "Extend": { 77 | "Monitors": [ 78 | { 79 | "UUID": "eDP-12fd580d2dc41168dce2efb1bf19adb54", 80 | "Name": "eDP-1", 81 | "Enabled": true, 82 | "X": 0, 83 | "Y": 0, 84 | "Width": 1920, 85 | "Height": 1080, 86 | "Rotation": 1, 87 | "Reflect": 0, 88 | "RefreshRate": 59.9987883403725, 89 | "Brightness": 1, 90 | "Primary": true 91 | } 92 | ], 93 | "ColorTemperatureMode": 0, 94 | "ColorTemperatureManual": 6500 95 | }, 96 | "Single": { 97 | "Monitors": { 98 | "UUID": "eDP-12fd580d2dc41168dce2efb1bf19adb54", 99 | "Name": "eDP-1", 100 | "Enabled": true, 101 | "X": 0, 102 | "Y": 0, 103 | "Width": 1920, 104 | "Height": 1080, 105 | "Rotation": 1, 106 | "Reflect": 0, 107 | "RefreshRate": 59.9987883403725, 108 | "Brightness": 0.74, 109 | "Primary": false 110 | }, 111 | "ColorTemperatureMode": 0, 112 | "ColorTemperatureManual": 6500 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /display/handle_event.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "time" 9 | 10 | x "github.com/linuxdeepin/go-x11-client" 11 | "github.com/linuxdeepin/go-x11-client/ext/randr" 12 | ) 13 | 14 | // 在 X 下,显示器属性改变,断开或者连接显示器。 15 | // 在 wayland 下,仅显示器属性改变。 16 | func (m *Manager) handleMonitorChanged(monitorInfo *MonitorInfo) { 17 | m.updateMonitor(monitorInfo) 18 | if _useWayland { 19 | return 20 | } 21 | 22 | // 后续只在 X 下需要 23 | currentNumMonitors := len(m.getConnectedMonitors()) 24 | m.PropsMu.Lock() 25 | prevCurrentNumMonitors := m.prevCurrentNumMonitors 26 | m.prevCurrentNumMonitors = currentNumMonitors 27 | 28 | if prevCurrentNumMonitors != currentNumMonitors { 29 | m.prevNumMonitors = prevCurrentNumMonitors 30 | m.prevNumMonitorsUpdatedAt = time.Now() 31 | } 32 | 33 | m.PropsMu.Unlock() 34 | 35 | logger.Debugf("prevCurrentNumMonitors: %v, currentNumMonitors: %v", prevCurrentNumMonitors, currentNumMonitors) 36 | var options applyOptions 37 | if currentNumMonitors < prevCurrentNumMonitors && currentNumMonitors >= 1 { 38 | // 连接状态的显示器数量减少了,并且现存一个及以上连接状态的显示器。 39 | logger.Debug("should disable crtc in apply") 40 | if options == nil { 41 | options = applyOptions{} 42 | } 43 | options[optionDisableCrtc] = true 44 | } 45 | m.updateMonitorsId(options) 46 | } 47 | 48 | // wayland 下连接显示器 49 | func (m *Manager) handleMonitorAdded(monitorInfo *MonitorInfo) { 50 | err := m.addMonitor(monitorInfo) 51 | if err != nil { 52 | logger.Warning(err) 53 | return 54 | } 55 | m.updatePropMonitors() 56 | m.updateMonitorsId(nil) 57 | } 58 | 59 | // wayland 下断开显示器 60 | func (m *Manager) handleMonitorRemoved(monitorId uint32) { 61 | logger.Debug("monitor removed", monitorId) 62 | monitor := m.removeMonitor(monitorId) 63 | if monitor == nil { 64 | logger.Warning("remove monitor failed, invalid id", monitorId) 65 | return 66 | } 67 | 68 | m.handleMonitorConnectedChanged(monitor, false) 69 | m.updatePropMonitors() 70 | m.updateMonitorsId(nil) 71 | } 72 | 73 | func (m *Manager) handleOutputPropertyChanged(ev *randr.OutputPropertyNotifyEvent) { 74 | logger.Debug("output property changed", ev.Output, ev.Atom) 75 | } 76 | 77 | func (m *Manager) handleScreenChanged(ev *randr.ScreenChangeNotifyEvent, cfgTsChanged bool) { 78 | width, height := ev.Width, ev.Height 79 | swapWidthHeightWithRotation(uint16(ev.Rotation), &width, &height) 80 | logger.Debugf("screen changed cfgTs: %v, rotation:%v, screen size: %vx%v", ev.ConfigTimestamp, 81 | ev.Rotation, width, height) 82 | 83 | m.PropsMu.Lock() 84 | m.setPropScreenWidth(width) 85 | m.setPropScreenHeight(height) 86 | m.PropsMu.Unlock() 87 | 88 | if cfgTsChanged { 89 | logger.Debug("config timestamp changed") 90 | if !_hasRandr1d2 { 91 | 92 | // randr 版本低于 1.2 93 | root := m.xConn.GetDefaultScreen().Root 94 | screenInfo, err := randr.GetScreenInfo(m.xConn, root).Reply(m.xConn) 95 | if err == nil { 96 | monitor := m.updateMonitorFallback(screenInfo) 97 | m.setPropPrimaryRect(x.Rectangle{ 98 | X: monitor.X, 99 | Y: monitor.Y, 100 | Width: monitor.Width, 101 | Height: monitor.Height, 102 | }) 103 | } else { 104 | logger.Warning(err) 105 | } 106 | } 107 | } 108 | 109 | logger.Info("redo map touch screen") 110 | m.handleTouchscreenChanged() 111 | 112 | if cfgTsChanged { 113 | m.showTouchscreenDialogs() 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /wl_display/config_v3_3.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "encoding/json" 9 | "os" 10 | "sort" 11 | "strings" 12 | ) 13 | 14 | type ScreenConfigV3_3 struct { 15 | Name string 16 | Primary string 17 | BaseInfos []*MonitorConfiV3_3 18 | } 19 | 20 | func (sc *ScreenConfigV3_3) toMonitorConfigs() []*MonitorConfig { 21 | result := make([]*MonitorConfig, len(sc.BaseInfos)) 22 | for idx, bi := range sc.BaseInfos { 23 | primary := bi.Name == sc.Primary 24 | result[idx] = &MonitorConfig{ 25 | UUID: bi.UUID, 26 | Name: bi.Name, 27 | Enabled: bi.Enabled, 28 | X: bi.X, 29 | Y: bi.Y, 30 | Width: bi.Width, 31 | Height: bi.Height, 32 | Rotation: bi.Rotation, 33 | Reflect: bi.Reflect, 34 | RefreshRate: bi.RefreshRate, 35 | Primary: primary, 36 | } 37 | } 38 | return result 39 | } 40 | 41 | type MonitorConfiV3_3 struct { 42 | UUID string // sum md5 of name and modes, for config 43 | Name string 44 | Enabled bool 45 | X int16 46 | Y int16 47 | Width uint16 48 | Height uint16 49 | Rotation uint16 50 | Reflect uint16 51 | RefreshRate float64 52 | } 53 | 54 | type ConfigV3_3 map[string]*ScreenConfigV3_3 55 | 56 | func loadConfigV3_3(filename string) (ConfigV3_3, error) { 57 | data, err := os.ReadFile(filename) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | var c ConfigV3_3 63 | err = json.Unmarshal(data, &c) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | return c, nil 69 | } 70 | 71 | func (c ConfigV3_3) toConfig() Config { 72 | newConfig := make(Config) 73 | 74 | for id, sc := range c { 75 | cfgKey := parseConfigKey(id) 76 | jId := cfgKey.getJoinedId() 77 | if cfgKey.name == "" { 78 | // 单屏幕,可设置分辨率 79 | if len(cfgKey.idFields) == 1 && 80 | len(sc.BaseInfos) == 1 { 81 | 82 | bi := sc.BaseInfos[0] 83 | newConfig[jId] = &ScreenConfig{ 84 | Custom: nil, 85 | Mirror: nil, 86 | Extend: nil, 87 | OnlyOne: nil, 88 | Single: &MonitorConfig{ 89 | UUID: bi.UUID, 90 | Name: bi.Name, 91 | Enabled: bi.Enabled, 92 | X: bi.X, 93 | Y: bi.Y, 94 | Width: bi.Width, 95 | Height: bi.Height, 96 | Rotation: bi.Rotation, 97 | Reflect: bi.Reflect, 98 | RefreshRate: bi.RefreshRate, 99 | Primary: true, 100 | }, 101 | } 102 | } 103 | } else { 104 | // custom mode 105 | screenCfg := newConfig[jId] 106 | if screenCfg == nil { 107 | screenCfg = &ScreenConfig{} 108 | newConfig[jId] = screenCfg 109 | } 110 | 111 | configs := sc.toMonitorConfigs() 112 | screenCfg.setMonitorConfigs(DisplayModeCustom, cfgKey.name, configs) 113 | } 114 | } 115 | return newConfig 116 | } 117 | 118 | type configKey struct { 119 | name string 120 | idFields []string 121 | } 122 | 123 | func (ck *configKey) getJoinedId() string { 124 | return strings.Join(ck.idFields, monitorsIdDelimiter) 125 | } 126 | 127 | func parseConfigKey(str string) configKey { 128 | var name string 129 | var idFields []string 130 | idx := strings.LastIndex(str, customModeDelim) 131 | if idx == -1 { 132 | idFields = strings.Split(str, monitorsIdDelimiter) 133 | } else { 134 | name = str[:idx] 135 | idFields = strings.Split(str[idx+1:], monitorsIdDelimiter) 136 | } 137 | 138 | sort.Strings(idFields) 139 | return configKey{ 140 | name: name, 141 | idFields: idFields, 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /wl_display/config_v3_3_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "encoding/json" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | const cfgStr0 = `{ 16 | "eDP12c5a5c24e4ab14126abf8dc36e7e9d4": { 17 | "Name": "", 18 | "Primary": "eDP-1", 19 | "BaseInfos": [ 20 | { 21 | "UUID": "eDP12c5a5c24e4ab14126abf8dc36e7e9d4", 22 | "Name": "eDP-1", 23 | "Enabled": true, 24 | "X": 0, 25 | "Y": 0, 26 | "Width": 1360, 27 | "Height": 768, 28 | "Rotation": 1, 29 | "Reflect": 0, 30 | "RefreshRate": 59.79899072004335, 31 | "MmWidth": 0, 32 | "MmHeight": 0 33 | } 34 | ] 35 | }, 36 | "_dde_display_config_private+HDMIf5cae317c40b01139be5af61896be0cf,eDP12c5a5c24e4ab14126abf8dc36e7e9d4": { 37 | "Name": "_dde_display_config_private", 38 | "Primary": "eDP-1", 39 | "BaseInfos": [ 40 | { 41 | "UUID": "eDP12c5a5c24e4ab14126abf8dc36e7e9d4", 42 | "Name": "eDP-1", 43 | "Enabled": true, 44 | "X": 0, 45 | "Y": 0, 46 | "Width": 1366, 47 | "Height": 768, 48 | "Rotation": 1, 49 | "Reflect": 0, 50 | "RefreshRate": 60.00471735199308, 51 | "MmWidth": 0, 52 | "MmHeight": 0 53 | }, 54 | { 55 | "UUID": "HDMIf5cae317c40b01139be5af61896be0cf", 56 | "Name": "HDMI-2", 57 | "Enabled": true, 58 | "X": 1366, 59 | "Y": 0, 60 | "Width": 1920, 61 | "Height": 1080, 62 | "Rotation": 1, 63 | "Reflect": 0, 64 | "RefreshRate": 60, 65 | "MmWidth": 0, 66 | "MmHeight": 0 67 | } 68 | ] 69 | } 70 | } 71 | ` 72 | 73 | func TestConfig(t *testing.T) { 74 | var cfg0 ConfigV3_3 75 | err := json.Unmarshal([]byte(cfgStr0), &cfg0) 76 | require.NoError(t, err) 77 | 78 | cfg := cfg0.toConfig() 79 | assert.Len(t, cfg, 2) 80 | 81 | screenCfg := cfg["eDP12c5a5c24e4ab14126abf8dc36e7e9d4"] 82 | assert.NotNil(t, screenCfg) 83 | assert.Equal(t, &MonitorConfig{ 84 | UUID: "eDP12c5a5c24e4ab14126abf8dc36e7e9d4", 85 | Name: "eDP-1", 86 | Enabled: true, 87 | X: 0, 88 | Y: 0, 89 | Width: 1360, 90 | Height: 768, 91 | Rotation: 1, 92 | Reflect: 0, 93 | RefreshRate: 59.79899072004335, 94 | Primary: true, 95 | }, screenCfg.Single) 96 | 97 | screenCfg = cfg["HDMIf5cae317c40b01139be5af61896be0cf,eDP12c5a5c24e4ab14126abf8dc36e7e9d4"] 98 | assert.NotNil(t, screenCfg) 99 | assert.Len(t, screenCfg.Custom, 1) 100 | customModeCfg := screenCfg.Custom[0] 101 | assert.Equal(t, "_dde_display_config_private", customModeCfg.Name) 102 | assert.Len(t, customModeCfg.Monitors, 2) 103 | assert.Equal(t, &MonitorConfig{ 104 | UUID: "eDP12c5a5c24e4ab14126abf8dc36e7e9d4", 105 | Name: "eDP-1", 106 | Enabled: true, 107 | X: 0, 108 | Y: 0, 109 | Width: 1366, 110 | Height: 768, 111 | Rotation: 1, 112 | Reflect: 0, 113 | RefreshRate: 60.00471735199308, 114 | Primary: true, 115 | }, customModeCfg.Monitors[0]) 116 | assert.Equal(t, &MonitorConfig{ 117 | UUID: "HDMIf5cae317c40b01139be5af61896be0cf", 118 | Name: "HDMI-2", 119 | Enabled: true, 120 | X: 1366, 121 | Y: 0, 122 | Width: 1920, 123 | Height: 1080, 124 | Rotation: 1, 125 | Reflect: 0, 126 | RefreshRate: 60, 127 | Primary: false, 128 | }, customModeCfg.Monitors[1]) 129 | } 130 | -------------------------------------------------------------------------------- /xsettings/xsettings_xresource.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package xsettings 6 | 7 | //#cgo pkg-config: x11 8 | //#cgo CFLAGS: -fstack-protector-strong -D_FORTITY_SOURCE=1 -fPIC 9 | //#include 10 | //#include 11 | //#include 12 | //#include 13 | //#include 14 | // 15 | //char * 16 | //get_xresources() 17 | //{ 18 | // Display *dpy = XOpenDisplay(NULL); 19 | // if (!dpy) { 20 | // return NULL; 21 | // } 22 | // 23 | // char *res = XResourceManagerString(dpy); 24 | // if (!res) { 25 | // XCloseDisplay(dpy); 26 | // fprintf(stderr, "No xresources data found!\n"); 27 | // return strdup("*customization:\t-color\n"); 28 | // } 29 | // 30 | // char *ret = strdup(res); 31 | // XCloseDisplay(dpy); 32 | // return ret; 33 | //} 34 | // 35 | //int 36 | //set_xresources(char *data, unsigned long length) 37 | //{ 38 | // Display *dpy = XOpenDisplay(NULL); 39 | // if (!dpy) { 40 | // return -1; 41 | // } 42 | // 43 | // XChangeProperty(dpy, DefaultRootWindow(dpy), XA_RESOURCE_MANAGER, XA_STRING, 8, PropModeReplace, 44 | // (const unsigned char*)data, length); 45 | // XCloseDisplay(dpy); 46 | // return 0; 47 | //} 48 | import "C" 49 | 50 | import ( 51 | "fmt" 52 | "strings" 53 | "unsafe" 54 | ) 55 | 56 | type xresourceInfo struct { 57 | key string 58 | value string 59 | } 60 | type xresourceInfos []*xresourceInfo 61 | 62 | func updateXResources(changes xresourceInfos) { 63 | var infos xresourceInfos 64 | res := C.get_xresources() 65 | data := C.GoString(res) 66 | C.free(unsafe.Pointer(res)) 67 | if len(data) == 0 { 68 | logger.Debug("--------No xresource found, created") 69 | infos = append(infos, &xresourceInfo{ 70 | key: "*customization", 71 | value: "-color", 72 | }) 73 | infos = append(infos, changes...) 74 | } else { 75 | logger.Debug("------------Info from read:", data) 76 | infos = unmarshalXResources(data) 77 | for _, v := range changes { 78 | logger.Debug("-----updateXResources info:", v.key, v.value) 79 | infos = infos.UpdateProperty(v.key, v.value) 80 | } 81 | } 82 | data = marshalXResources(infos) 83 | logger.Debug("[updateXResources] will set to:", data) 84 | res = C.CString(data) 85 | defer C.free(unsafe.Pointer(res)) 86 | ret := C.set_xresources(res, C.ulong(len(data))) 87 | if ret != C.int(0) { 88 | logger.Error("Failed to set xresource:", data) 89 | } 90 | } 91 | 92 | func (infos xresourceInfos) UpdateProperty(key, value string) xresourceInfos { 93 | info := infos.Get(key) 94 | if info == nil { 95 | infos = append(infos, &xresourceInfo{ 96 | key: key, 97 | value: value, 98 | }) 99 | return infos 100 | } 101 | 102 | info.value = value 103 | return infos 104 | } 105 | 106 | func (infos xresourceInfos) Get(key string) *xresourceInfo { 107 | for _, info := range infos { 108 | if info.key == key { 109 | return info 110 | } 111 | } 112 | return nil 113 | } 114 | 115 | func marshalXResources(infos xresourceInfos) string { 116 | var data string 117 | for _, info := range infos { 118 | data += info.key + ":\t" + info.value + "\n" 119 | } 120 | return data 121 | } 122 | 123 | func unmarshalXResources(data string) xresourceInfos { 124 | lines := strings.Split(data, "\n") 125 | var infos xresourceInfos 126 | for _, line := range lines { 127 | if len(line) == 0 { 128 | continue 129 | } 130 | array := strings.Split(line, ":\t") 131 | if len(array) != 2 { 132 | fmt.Println("Array:", array) 133 | continue 134 | } 135 | 136 | infos = append(infos, &xresourceInfo{ 137 | key: array[0], 138 | value: array[1], 139 | }) 140 | } 141 | return infos 142 | } 143 | -------------------------------------------------------------------------------- /wl_display/exported_methods_auto.go: -------------------------------------------------------------------------------- 1 | // Code generated by "dbusutil-gen em -type Manager,Monitor"; DO NOT EDIT. 2 | 3 | package display 4 | 5 | import ( 6 | "github.com/linuxdeepin/go-lib/dbusutil" 7 | ) 8 | 9 | func (v *Manager) GetExportedMethods() dbusutil.ExportedMethods { 10 | return dbusutil.ExportedMethods{ 11 | { 12 | Name: "ApplyChanges", 13 | Fn: v.ApplyChanges, 14 | }, 15 | { 16 | Name: "AssociateTouch", 17 | Fn: v.AssociateTouch, 18 | InArgs: []string{"outputName", "touch"}, 19 | }, 20 | { 21 | Name: "CanRotate", 22 | Fn: v.CanRotate, 23 | OutArgs: []string{"outArg0"}, 24 | }, 25 | { 26 | Name: "CanSetBrightness", 27 | Fn: v.CanSetBrightness, 28 | InArgs: []string{"outputName"}, 29 | OutArgs: []string{"outArg0"}, 30 | }, 31 | { 32 | Name: "CanSwitchMode", 33 | Fn: v.CanSwitchMode, 34 | OutArgs: []string{"outArg0"}, 35 | }, 36 | { 37 | Name: "ChangeBrightness", 38 | Fn: v.ChangeBrightness, 39 | InArgs: []string{"raised"}, 40 | }, 41 | { 42 | Name: "DeleteCustomMode", 43 | Fn: v.DeleteCustomMode, 44 | InArgs: []string{"name"}, 45 | }, 46 | { 47 | Name: "GetBrightness", 48 | Fn: v.GetBrightness, 49 | OutArgs: []string{"outArg0"}, 50 | }, 51 | { 52 | Name: "GetCustomDisplayMode", 53 | Fn: v.GetCustomDisplayMode, 54 | OutArgs: []string{"outArg0"}, 55 | }, 56 | { 57 | Name: "GetRealDisplayMode", 58 | Fn: v.GetRealDisplayMode, 59 | OutArgs: []string{"outArg0"}, 60 | }, 61 | { 62 | Name: "ListOutputNames", 63 | Fn: v.ListOutputNames, 64 | OutArgs: []string{"outArg0"}, 65 | }, 66 | { 67 | Name: "ListOutputsCommonModes", 68 | Fn: v.ListOutputsCommonModes, 69 | OutArgs: []string{"outArg0"}, 70 | }, 71 | { 72 | Name: "ModifyConfigName", 73 | Fn: v.ModifyConfigName, 74 | InArgs: []string{"name", "newName"}, 75 | }, 76 | { 77 | Name: "RefreshBrightness", 78 | Fn: v.RefreshBrightness, 79 | }, 80 | { 81 | Name: "Reset", 82 | Fn: v.Reset, 83 | }, 84 | { 85 | Name: "ResetChanges", 86 | Fn: v.ResetChanges, 87 | }, 88 | { 89 | Name: "Save", 90 | Fn: v.Save, 91 | }, 92 | { 93 | Name: "SetAndSaveBrightness", 94 | Fn: v.SetAndSaveBrightness, 95 | InArgs: []string{"outputName", "value"}, 96 | }, 97 | { 98 | Name: "SetBrightness", 99 | Fn: v.SetBrightness, 100 | InArgs: []string{"outputName", "value"}, 101 | }, 102 | { 103 | Name: "SetCustomDisplayMode", 104 | Fn: v.SetCustomDisplayMode, 105 | InArgs: []string{"mode"}, 106 | }, 107 | { 108 | Name: "SetPrimary", 109 | Fn: v.SetPrimary, 110 | InArgs: []string{"outputName"}, 111 | }, 112 | { 113 | Name: "SwitchMode", 114 | Fn: v.SwitchMode, 115 | InArgs: []string{"mode", "name"}, 116 | }, 117 | } 118 | } 119 | func (v *Monitor) GetExportedMethods() dbusutil.ExportedMethods { 120 | return dbusutil.ExportedMethods{ 121 | { 122 | Name: "Enable", 123 | Fn: v.Enable, 124 | InArgs: []string{"enabled"}, 125 | }, 126 | { 127 | Name: "SetMode", 128 | Fn: v.SetMode, 129 | InArgs: []string{"mode"}, 130 | }, 131 | { 132 | Name: "SetModeBySize", 133 | Fn: v.SetModeBySize, 134 | InArgs: []string{"width", "height"}, 135 | }, 136 | { 137 | Name: "SetPosition", 138 | Fn: v.SetPosition, 139 | InArgs: []string{"X", "y"}, 140 | }, 141 | { 142 | Name: "SetReflect", 143 | Fn: v.SetReflect, 144 | InArgs: []string{"value"}, 145 | }, 146 | { 147 | Name: "SetRefreshRate", 148 | Fn: v.SetRefreshRate, 149 | InArgs: []string{"value"}, 150 | }, 151 | { 152 | Name: "SetRotation", 153 | Fn: v.SetRotation, 154 | InArgs: []string{"value"}, 155 | }, 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /display/config.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "encoding/gob" 9 | "encoding/json" 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | "time" 14 | 15 | "github.com/linuxdeepin/go-lib/xdg/basedir" 16 | ) 17 | 18 | var ( 19 | // v1 ~ v4 旧版本配置文件,~/.config/deepin/startdde/display.json 20 | configFile string 21 | // v5 v6 版本配置文件, ~/.config/deepin/startdde/display_v5.json 22 | configFileV5 string 23 | // ~/.config/deepin/startdde/config.version 24 | // 之前最新版本号 5.0 25 | configVersionFile string 26 | // 用户级别配置文件 ~/.config/deepin/startdde/display-user.json 27 | userConfigFile string 28 | ) 29 | 30 | func init() { 31 | cfgDir := getCfgDir() 32 | configFile = filepath.Join(cfgDir, "display.json") 33 | configFileV5 = filepath.Join(cfgDir, "display_v5.json") 34 | configVersionFile = filepath.Join(cfgDir, "config.version") 35 | userConfigFile = filepath.Join(cfgDir, "display-user.json") 36 | } 37 | 38 | func getCfgDir() string { 39 | return filepath.Join(basedir.GetUserConfigDir(), "deepin/startdde") 40 | } 41 | 42 | func updateSysMonitorConfigsName(configs SysMonitorConfigs, monitorMap map[uint32]*Monitor) { 43 | for _, mc := range configs { 44 | for _, m := range monitorMap { 45 | if mc.UUID == m.uuid { 46 | mc.Name = m.Name 47 | break 48 | } 49 | } 50 | } 51 | } 52 | 53 | func loadBuiltinMonitorConfig(filename string) (string, error) { 54 | // #nosec G304 55 | content, err := os.ReadFile(filename) 56 | if err != nil { 57 | return "", err 58 | } 59 | return strings.TrimSpace(string(content)), nil 60 | } 61 | 62 | type ConnectInfo struct { 63 | Connects map[string]bool 64 | LastConnectedTimes map[string]time.Time 65 | } 66 | 67 | func readConnectInfoCache(file string) (*ConnectInfo, error) { 68 | // #nosec G304 69 | fp, err := os.Open(file) 70 | if err != nil { 71 | return nil, err 72 | } 73 | defer func() { 74 | _ = fp.Close() 75 | }() 76 | 77 | var info ConnectInfo 78 | decoder := gob.NewDecoder(fp) 79 | err = decoder.Decode(&info) 80 | if err != nil { 81 | return nil, err 82 | } 83 | return &info, nil 84 | } 85 | 86 | // 加载 v5 或 v6 配置,v6 优先 87 | func loadConfigV5V6(filename string) (*ConfigV6, error) { 88 | // #nosec G304 89 | data, err := os.ReadFile(filename) 90 | if err != nil { 91 | return nil, err 92 | } 93 | 94 | var c ConfigV6 95 | err = json.Unmarshal(data, &c) 96 | if err != nil { 97 | logger.Warning(err) 98 | } 99 | 100 | // 以 v6 配置格式解析失败,尝试以 v5 配置格式解析。 101 | if c.ConfigV5 == nil { 102 | err = json.Unmarshal(data, &c.ConfigV5) 103 | if err != nil { 104 | return nil, err 105 | } 106 | } 107 | return &c, nil 108 | } 109 | 110 | // 加载旧配置文件,可能影响 m.DisplayMode 和系统配置中的 DisplayMode。 111 | func loadOldConfig(m *Manager) (*ConfigV6, error) { 112 | var configV5 ConfigV5 113 | cfgVer, err := getConfigVersion(configVersionFile) 114 | if err == nil { 115 | if cfgVer == "3.3" { 116 | // 3.3配置文件转换 117 | cfg0, err := loadConfigV3D3(configFile) 118 | if err == nil { 119 | configV5 = cfg0.toConfig(m) 120 | } else if !os.IsNotExist(err) { 121 | logger.Warning(err) 122 | } 123 | } else if cfgVer == "4.0" { 124 | // 4.0配置文件转换 125 | cfg0, err := loadConfigV4(configFile) 126 | if err == nil { 127 | configV5 = cfg0.toConfig(m) 128 | } else if !os.IsNotExist(err) { 129 | logger.Warning(err) 130 | } 131 | } 132 | } else if !os.IsNotExist(err) { 133 | logger.Warning(err) 134 | } 135 | 136 | if len(configV5) > 0 { 137 | // 加载 v5 之前配置文件成功 138 | configV6 := &ConfigV6{ConfigV5: configV5} 139 | return configV6, nil 140 | } 141 | // NOTE: v5 到 v6 并没有更新 configVersionFile 的内容, 也使用同一个配置文件。 142 | configV6, err := loadConfigV5V6(configFileV5) 143 | if err != nil { 144 | // 加载 v5 和 v6 配置文件都失败 145 | return nil, err 146 | } 147 | // 加载 v5 或 v6 配置文件成功 148 | return configV6, nil 149 | } 150 | 151 | func (m *Manager) saveBuiltinMonitorConfig(name string) (err error) { 152 | m.sysConfig.mu.Lock() 153 | 154 | m.sysConfig.Config.Cache.BuiltinMonitor = name 155 | err = m.saveSysConfigNoLock("builtin monitor") 156 | 157 | m.sysConfig.mu.Unlock() 158 | return 159 | } 160 | -------------------------------------------------------------------------------- /display/testdata/display_v5.json: -------------------------------------------------------------------------------- 1 | { 2 | "HDMI-1bc06f293ee6bfb16fd813648741f8ac3,eDP-12fd580d2dc41168dce2efb1bf19adb54": { 3 | "Extend": { 4 | "Monitors": [ 5 | { 6 | "UUID": "eDP-12fd580d2dc41168dce2efb1bf19adb54", 7 | "Name": "eDP-1", 8 | "Enabled": true, 9 | "X": 0, 10 | "Y": 0, 11 | "Width": 1920, 12 | "Height": 1080, 13 | "Rotation": 1, 14 | "Reflect": 0, 15 | "RefreshRate": 59.9987883403725, 16 | "Brightness": 1, 17 | "Primary": true, 18 | "ColorTemperatureMode": 0, 19 | "ColorTemperatureManual": 6500 20 | }, 21 | { 22 | "UUID": "HDMI-1bc06f293ee6bfb16fd813648741f8ac3", 23 | "Name": "HDMI-1", 24 | "Enabled": true, 25 | "X": 1920, 26 | "Y": 0, 27 | "Width": 3840, 28 | "Height": 2160, 29 | "Rotation": 1, 30 | "Reflect": 0, 31 | "RefreshRate": 30, 32 | "Brightness": 1, 33 | "Primary": false, 34 | "ColorTemperatureMode": 0, 35 | "ColorTemperatureManual": 6500 36 | } 37 | ] 38 | } 39 | }, 40 | "HDMI-1c77daa1f6cbe84180d58423d5909fbd9,eDP-12fd580d2dc41168dce2efb1bf19adb54": { 41 | "Extend": { 42 | "Monitors": [ 43 | { 44 | "UUID": "eDP-12fd580d2dc41168dce2efb1bf19adb54", 45 | "Name": "eDP-1", 46 | "Enabled": true, 47 | "X": 0, 48 | "Y": 0, 49 | "Width": 1920, 50 | "Height": 1080, 51 | "Rotation": 1, 52 | "Reflect": 0, 53 | "RefreshRate": 59.9987883403725, 54 | "Brightness": 1, 55 | "Primary": true, 56 | "ColorTemperatureMode": 0, 57 | "ColorTemperatureManual": 6500 58 | }, 59 | { 60 | "UUID": "HDMI-1c77daa1f6cbe84180d58423d5909fbd9", 61 | "Name": "HDMI-1", 62 | "Enabled": true, 63 | "X": 1920, 64 | "Y": 0, 65 | "Width": 4096, 66 | "Height": 2160, 67 | "Rotation": 1, 68 | "Reflect": 0, 69 | "RefreshRate": 30, 70 | "Brightness": 1, 71 | "Primary": false, 72 | "ColorTemperatureMode": 0, 73 | "ColorTemperatureManual": 6500 74 | } 75 | ] 76 | } 77 | }, 78 | "HDMI-1eda2eb854f46f7d8fc7ce7452cce10ed,eDP-12fd580d2dc41168dce2efb1bf19adb54": { 79 | "Extend": { 80 | "Monitors": [ 81 | { 82 | "UUID": "eDP-12fd580d2dc41168dce2efb1bf19adb54", 83 | "Name": "eDP-1", 84 | "Enabled": true, 85 | "X": 0, 86 | "Y": 0, 87 | "Width": 1920, 88 | "Height": 1080, 89 | "Rotation": 1, 90 | "Reflect": 0, 91 | "RefreshRate": 59.9987883403725, 92 | "Brightness": 0.63, 93 | "Primary": true, 94 | "ColorTemperatureMode": 0, 95 | "ColorTemperatureManual": 6500 96 | }, 97 | { 98 | "UUID": "HDMI-1eda2eb854f46f7d8fc7ce7452cce10ed", 99 | "Name": "HDMI-1", 100 | "Enabled": true, 101 | "X": 1920, 102 | "Y": 0, 103 | "Width": 1920, 104 | "Height": 1080, 105 | "Rotation": 1, 106 | "Reflect": 0, 107 | "RefreshRate": 60, 108 | "Brightness": 0.74, 109 | "Primary": false, 110 | "ColorTemperatureMode": 0, 111 | "ColorTemperatureManual": 6500 112 | } 113 | ] 114 | } 115 | }, 116 | "eDP-12fd580d2dc41168dce2efb1bf19adb54": { 117 | "Single": { 118 | "Monitor": { 119 | "UUID": "eDP-12fd580d2dc41168dce2efb1bf19adb54", 120 | "Name": "eDP-1", 121 | "Enabled": true, 122 | "X": 0, 123 | "Y": 0, 124 | "Width": 1920, 125 | "Height": 1080, 126 | "Rotation": 1, 127 | "Reflect": 0, 128 | "RefreshRate": 59.9987883403725, 129 | "Brightness": 0.4032, 130 | "Primary": false, 131 | "ColorTemperatureMode": 0, 132 | "ColorTemperatureManual": 0 133 | }, 134 | "ColorTemperatureMode": 0, 135 | "ColorTemperatureManual": 7400 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /misc/schemas/com.deepin.dde.display.gschema.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 'auto' 25 | the method for setting brightness 26 | the method for setting brightness. 27 | 28 | 29 | 'extend' 30 | the multi monitors display mode 31 | The mode of multi monitors 32 | 33 | 34 | '' 35 | the outputs brightness 36 | The monitor brightness 37 | 38 | 39 | '' 40 | Map output to monitor 41 | Output monitor map 42 | 43 | 44 | '{"1002:6611": {"1920*1080": [59.94, 30, 29.97, 25, 24, 23.98],"1680*945": [60.02]},"1002:6779": {"1920*1080": [30,29.97,25,24,23.98]}}' 45 | Screen refresh rate filter 46 | 47 | Screen refresh rate filter in JSON. 48 | You could config this conveniently by 49 | https://gitlabwh.uniontech.com/ut001384/display-rate-filter 50 | 51 | 52 | 53 | '' 54 | the primary output 55 | The primary output 56 | 57 | 58 | '' 59 | the current custom id 60 | The id of config only in custom mode 61 | 62 | 63 | [] 64 | the output blacklist 65 | Filter some invalid outputs 66 | 67 | 68 | [] 69 | The output priority 70 | Outputs priority, the first will be primary 71 | 72 | 73 | 'normal' 74 | method of adjust color temperature according to current time and location 75 | 76 | 77 | 6500 78 | 79 | current color temperature when manual adjustment 80 | 81 | 82 | 500 83 | 84 | Rotate the screen when the delay ends 85 | 86 | 87 | 1 88 | 89 | custom display mode 90 | The mode of custom display 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /display/exported_methods_auto.go: -------------------------------------------------------------------------------- 1 | // Code generated by "dbusutil-gen em -type Manager,Monitor"; DO NOT EDIT. 2 | 3 | package display 4 | 5 | import ( 6 | "github.com/linuxdeepin/go-lib/dbusutil" 7 | ) 8 | 9 | func (v *Manager) GetExportedMethods() dbusutil.ExportedMethods { 10 | return dbusutil.ExportedMethods{ 11 | { 12 | Name: "ApplyChanges", 13 | Fn: v.ApplyChanges, 14 | }, 15 | { 16 | Name: "AssociateTouch", 17 | Fn: v.AssociateTouch, 18 | InArgs: []string{"outputName", "touchSerial"}, 19 | }, 20 | { 21 | Name: "AssociateTouchByUUID", 22 | Fn: v.AssociateTouchByUUID, 23 | InArgs: []string{"outputName", "touchUUID"}, 24 | }, 25 | { 26 | Name: "CanRotate", 27 | Fn: v.CanRotate, 28 | OutArgs: []string{"outArg0"}, 29 | }, 30 | { 31 | Name: "CanSetBrightness", 32 | Fn: v.CanSetBrightness, 33 | InArgs: []string{"outputName"}, 34 | OutArgs: []string{"outArg0"}, 35 | }, 36 | { 37 | Name: "ChangeBrightness", 38 | Fn: v.ChangeBrightness, 39 | InArgs: []string{"raised"}, 40 | }, 41 | { 42 | Name: "DeleteCustomMode", 43 | Fn: v.DeleteCustomMode, 44 | InArgs: []string{"name"}, 45 | }, 46 | { 47 | Name: "GetBrightness", 48 | Fn: v.GetBrightness, 49 | OutArgs: []string{"outArg0"}, 50 | }, 51 | { 52 | Name: "GetBuiltinMonitor", 53 | Fn: v.GetBuiltinMonitor, 54 | OutArgs: []string{"outArg0", "outArg1"}, 55 | }, 56 | { 57 | Name: "GetRealDisplayMode", 58 | Fn: v.GetRealDisplayMode, 59 | OutArgs: []string{"outArg0"}, 60 | }, 61 | { 62 | Name: "ListOutputNames", 63 | Fn: v.ListOutputNames, 64 | OutArgs: []string{"outArg0"}, 65 | }, 66 | { 67 | Name: "ListOutputsCommonModes", 68 | Fn: v.ListOutputsCommonModes, 69 | OutArgs: []string{"outArg0"}, 70 | }, 71 | { 72 | Name: "ModifyConfigName", 73 | Fn: v.ModifyConfigName, 74 | InArgs: []string{"name", "newName"}, 75 | }, 76 | { 77 | Name: "RefreshBrightness", 78 | Fn: v.RefreshBrightness, 79 | }, 80 | { 81 | Name: "Reset", 82 | Fn: v.Reset, 83 | }, 84 | { 85 | Name: "ResetChanges", 86 | Fn: v.ResetChanges, 87 | }, 88 | { 89 | Name: "Save", 90 | Fn: v.Save, 91 | }, 92 | { 93 | Name: "SetAndSaveBrightness", 94 | Fn: v.SetAndSaveBrightness, 95 | InArgs: []string{"outputName", "value"}, 96 | }, 97 | { 98 | Name: "SetBrightness", 99 | Fn: v.SetBrightness, 100 | InArgs: []string{"outputName", "value"}, 101 | }, 102 | { 103 | Name: "SetColorTemperature", 104 | Fn: v.SetColorTemperature, 105 | InArgs: []string{"value"}, 106 | }, 107 | { 108 | Name: "SetCustomColorTempTimePeriod", 109 | Fn: v.SetCustomColorTempTimePeriod, 110 | InArgs: []string{"timePeriod"}, 111 | }, 112 | { 113 | Name: "SetMethodAdjustCCT", 114 | Fn: v.SetMethodAdjustCCT, 115 | InArgs: []string{"adjustMethod"}, 116 | }, 117 | { 118 | Name: "SetPrimary", 119 | Fn: v.SetPrimary, 120 | InArgs: []string{"outputName"}, 121 | }, 122 | { 123 | Name: "SupportSetColorTemperature", 124 | Fn: v.SupportSetColorTemperature, 125 | OutArgs: []string{"outArg0"}, 126 | }, 127 | { 128 | Name: "SwitchMode", 129 | Fn: v.SwitchMode, 130 | InArgs: []string{"mode", "name"}, 131 | }, 132 | } 133 | } 134 | func (v *Monitor) GetExportedMethods() dbusutil.ExportedMethods { 135 | return dbusutil.ExportedMethods{ 136 | { 137 | Name: "Enable", 138 | Fn: v.Enable, 139 | InArgs: []string{"enabled"}, 140 | }, 141 | { 142 | Name: "SetMode", 143 | Fn: v.SetMode, 144 | InArgs: []string{"mode"}, 145 | }, 146 | { 147 | Name: "SetModeBySize", 148 | Fn: v.SetModeBySize, 149 | InArgs: []string{"width", "height"}, 150 | }, 151 | { 152 | Name: "SetPosition", 153 | Fn: v.SetPosition, 154 | InArgs: []string{"X", "y"}, 155 | }, 156 | { 157 | Name: "SetReflect", 158 | Fn: v.SetReflect, 159 | InArgs: []string{"value"}, 160 | }, 161 | { 162 | Name: "SetRefreshRate", 163 | Fn: v.SetRefreshRate, 164 | InArgs: []string{"value"}, 165 | }, 166 | { 167 | Name: "SetRotation", 168 | Fn: v.SetRotation, 169 | InArgs: []string{"value"}, 170 | }, 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /display/display.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "errors" 9 | "reflect" 10 | 11 | "github.com/godbus/dbus/v5" 12 | sysdisplay "github.com/linuxdeepin/go-dbus-factory/system/org.deepin.dde.display1" 13 | "github.com/linuxdeepin/go-lib/dbusutil" 14 | "github.com/linuxdeepin/go-lib/log" 15 | ) 16 | 17 | var logger = log.NewLogger("daemon/display") 18 | 19 | const ( 20 | dbusServiceName = "org.deepin.dde.Display1" 21 | dbusInterface = "org.deepin.dde.Display1" 22 | dbusPath = "/org/deepin/dde/Display1" 23 | ) 24 | 25 | var _dpy *Manager 26 | 27 | var _greeterMode bool 28 | 29 | func SetGreeterMode(val bool) { 30 | _greeterMode = val 31 | } 32 | 33 | type scaleFactorsHelper struct { 34 | changedCb func(factors map[string]float64) error 35 | } 36 | 37 | // ScaleFactorsHelper 全局的 scale factors 相关 helper,要传给 xsettings 模块。 38 | var ScaleFactorsHelper scaleFactorsHelper 39 | 40 | // 用于在 display.Start 还没被调用时,先由 xsettings.Start 调用了 ScaleFactorsHelper.SetScaleFactors, 缓存数据。 41 | var _scaleFactors map[string]float64 42 | 43 | func (h *scaleFactorsHelper) SetScaleFactors(factors map[string]float64) error { 44 | if _dpy == nil { 45 | _scaleFactors = factors 46 | return nil 47 | } 48 | return _dpy.setScaleFactors(factors) 49 | } 50 | 51 | func (h *scaleFactorsHelper) GetScaleFactors() (map[string]float64, error) { 52 | sysBus, err := dbus.SystemBus() 53 | if err != nil { 54 | return nil, err 55 | } 56 | sysDisplay := sysdisplay.NewDisplay(sysBus) 57 | cfgJson, err := sysDisplay.GetConfig(0) 58 | if err != nil { 59 | return nil, err 60 | } 61 | var rootCfg struct { 62 | Config struct { 63 | ScaleFactors map[string]float64 64 | } 65 | } 66 | err = jsonUnmarshal(cfgJson, &rootCfg) 67 | if err != nil { 68 | return nil, err 69 | } 70 | return rootCfg.Config.ScaleFactors, nil 71 | } 72 | 73 | func (h *scaleFactorsHelper) SetChangedCb(fn func(factors map[string]float64) error) { 74 | h.changedCb = fn 75 | } 76 | 77 | func (m *Manager) setScaleFactors(factors map[string]float64) error { 78 | logger.Debug("setScaleFactors", factors) 79 | m.sysConfig.mu.Lock() 80 | defer m.sysConfig.mu.Unlock() 81 | 82 | if reflect.DeepEqual(m.sysConfig.Config.ScaleFactors, factors) { 83 | return nil 84 | } 85 | m.sysConfig.Config.ScaleFactors = factors 86 | err := m.saveSysConfigNoLock("scale factors changed") 87 | if err != nil { 88 | logger.Warning(err) 89 | } 90 | return err 91 | } 92 | 93 | func Start(service *dbusutil.Service) error { 94 | m := newManager(service) 95 | m.init() 96 | 97 | if !_greeterMode { 98 | // 正常 startdde 99 | err := service.Export(dbusPath, m) 100 | if err != nil { 101 | return err 102 | } 103 | so := service.GetServerObject(m) 104 | if so != nil { 105 | err = so.SetWriteCallback(m, "ColorTemperatureEnabled", func(write *dbusutil.PropertyWrite) *dbus.Error { 106 | value, ok := write.Value.(bool) 107 | if !ok { 108 | err = errors.New("Type is not bool") 109 | logger.Warning(err) 110 | return dbusutil.ToError(err) 111 | } 112 | ok = m.setPropColorTemperatureEnabled(value) 113 | if !ok { 114 | err = errors.New("Set ColorTemperatureEnabled failed!") 115 | logger.Warning(err) 116 | return dbusutil.ToError(err) 117 | } 118 | cfg := m.getSuitableUserMonitorModeConfig(m.DisplayMode) 119 | if cfg == nil { 120 | cfg = getDefaultUserMonitorModeConfig() 121 | } 122 | mode := ColorTemperatureModeNone 123 | if value { 124 | mode = m.colorTemperatureModeOn 125 | } else { 126 | m.colorTemperatureModeOn = m.ColorTemperatureMode 127 | } 128 | err = m.setColorTempMode(mode) 129 | return dbusutil.ToError(err) 130 | }) 131 | } 132 | 133 | err = service.RequestName(dbusServiceName) 134 | if err != nil { 135 | return err 136 | } 137 | } 138 | _dpy = m 139 | return nil 140 | } 141 | 142 | func StartPart2() error { 143 | if _dpy == nil { 144 | return errors.New("_dpy is nil") 145 | } 146 | m := _dpy 147 | m.initSysDisplay() 148 | m.initTouchscreens() 149 | 150 | if !_greeterMode { 151 | controlRedshift("disable") 152 | m.applyColorTempConfig(m.DisplayMode) 153 | } 154 | 155 | return nil 156 | } 157 | 158 | func SetLogLevel(level log.Priority) { 159 | logger.SetLogLevel(level) 160 | } 161 | -------------------------------------------------------------------------------- /wl_display/display_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func Test_getMaxAreaSize(t *testing.T) { 14 | size := getMaxAreaSize([]Size{ 15 | {1024, 768}, 16 | {640, 480}, 17 | {1280, 720}, 18 | {800, 600}, 19 | }) 20 | assert.Equal(t, Size{1280, 720}, size) 21 | size = getMaxAreaSize(nil) 22 | assert.Equal(t, Size{}, size) 23 | size = getMaxAreaSize([]Size{ 24 | {1024, 768}, 25 | }) 26 | assert.Equal(t, Size{1024, 768}, size) 27 | } 28 | 29 | func Test_filterModeInfos(t *testing.T) { 30 | modes := []ModeInfo{ 31 | { 32 | Id: 1, 33 | name: "1024x768", 34 | Width: 1024, 35 | Height: 768, 36 | Rate: 60.1, 37 | }, 38 | { 39 | Id: 2, 40 | name: "1024x768i", 41 | Width: 1024, 42 | Height: 768, 43 | Rate: 60.1, 44 | }, 45 | { 46 | Id: 3, 47 | name: "1024x768i", 48 | Width: 1024, 49 | Height: 768, 50 | Rate: 60.3, 51 | }, 52 | } 53 | assert.Equal(t, []ModeInfo{ 54 | { 55 | Id: 1, 56 | name: "1024x768", 57 | Width: 1024, 58 | Height: 768, 59 | Rate: 60.1, 60 | }, 61 | }, filterModeInfos(modes)) 62 | 63 | // -------------------------- 64 | modes = []ModeInfo{ 65 | { 66 | Id: 1, 67 | name: "1024x768", 68 | Width: 1024, 69 | Height: 768, 70 | Rate: 60.1, 71 | }, 72 | { 73 | Id: 2, 74 | name: "1024x768", 75 | Width: 1024, 76 | Height: 768, 77 | Rate: 60.10000001, 78 | }, 79 | { 80 | Id: 3, 81 | name: "1024x768", 82 | Width: 1024, 83 | Height: 768, 84 | Rate: 60.3, 85 | }, 86 | } 87 | assert.Equal(t, []ModeInfo{ 88 | { 89 | Id: 1, 90 | name: "1024x768", 91 | Width: 1024, 92 | Height: 768, 93 | Rate: 60.1, 94 | }, 95 | { 96 | Id: 3, 97 | name: "1024x768", 98 | Width: 1024, 99 | Height: 768, 100 | Rate: 60.3, 101 | }, 102 | }, filterModeInfos(modes)) 103 | 104 | // -------------------------- 105 | // 混合 106 | modes = []ModeInfo{ 107 | { 108 | Id: 1, 109 | name: "1024x768", 110 | Width: 1024, 111 | Height: 768, 112 | Rate: 60.1, 113 | }, 114 | { 115 | Id: 2, 116 | name: "1024x768", 117 | Width: 1024, 118 | Height: 768, 119 | Rate: 60.10000001, 120 | }, 121 | { 122 | Id: 3, 123 | name: "1024x768", 124 | Width: 1024, 125 | Height: 768, 126 | Rate: 60.3, 127 | }, 128 | { 129 | Id: 4, 130 | name: "1024x768i", 131 | Width: 1024, 132 | Height: 768, 133 | Rate: 60.1, 134 | }, 135 | { 136 | Id: 5, 137 | name: "1024x768i", 138 | Width: 1024, 139 | Height: 768, 140 | Rate: 60.3, 141 | }, 142 | } 143 | assert.Equal(t, []ModeInfo{ 144 | { 145 | Id: 1, 146 | name: "1024x768", 147 | Width: 1024, 148 | Height: 768, 149 | Rate: 60.1, 150 | }, 151 | { 152 | Id: 3, 153 | name: "1024x768", 154 | Width: 1024, 155 | Height: 768, 156 | Rate: 60.3, 157 | }, 158 | }, filterModeInfos(modes)) 159 | } 160 | 161 | func TestCalcRecommendedScaleFactor(t *testing.T) { 162 | for _, rec := range []struct { 163 | widthPx float64 164 | heightPx float64 165 | widthMm float64 166 | heightMm float64 167 | expect float64 168 | }{ 169 | {1366, 768, 310, 147, 1}, 170 | {1366, 768, 277, 165, 1}, 171 | {1366, 768, 309, 174, 1}, 172 | 173 | {1600, 900, 294, 166, 1}, 174 | 175 | {1920, 1080, 344, 194, 1.25}, 176 | {1920, 1080, 477, 268, 1}, 177 | {1920, 1080, 527, 296, 1}, 178 | {1920, 1080, 476, 268, 1}, 179 | {1920, 1080, 520, 310, 1}, 180 | {1920, 1080, 708, 398, 1}, 181 | {1920, 1080, 518, 324, 1}, 182 | {1920, 1080, 510, 287, 1}, 183 | {1920, 1080, 527, 296, 1}, 184 | {1920, 1080, 309, 174, 1.25}, 185 | {1920, 1080, 293, 165, 1.25}, 186 | {1920, 1080, 294, 165, 1.25}, 187 | 188 | {2160, 1440, 280, 180, 1.5}, 189 | 190 | {3000, 2000, 290, 200, 2}, 191 | 192 | {3840, 2160, 600, 340, 2}, 193 | {3840, 2160, 344, 193, 2.25}, 194 | } { 195 | factor := calcRecommendedScaleFactor(rec.widthPx, rec.heightPx, rec.widthMm, rec.heightMm) 196 | assert.Equal(t, rec.expect, factor, "%gx%g %gmm x %gmm", 197 | rec.widthPx, rec.heightPx, rec.widthMm, rec.heightMm) 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /wl_display/rect.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "fmt" 9 | "math" 10 | 11 | x "github.com/linuxdeepin/go-x11-client" 12 | ) 13 | 14 | const INT_MAX = int(^uint(0) >> 1) 15 | 16 | const ( 17 | rectPoint0 int = iota 18 | rectPoint1 19 | rectPoint2 20 | rectPoint3 21 | ) 22 | 23 | type Point struct { 24 | X int 25 | Y int 26 | } 27 | 28 | func (m *Manager) intersects(m0, m1 x.Rectangle) bool { 29 | mid0X := (m0.X + m0.X + int16(m0.Width)) / 2 30 | mid0Y := (m0.Y + m0.Y + int16(m0.Height)) / 2 31 | mid1X := (m1.X + m1.X + int16(m1.Width)) / 2 32 | mid1Y := (m1.Y + m1.Y + int16(m1.Height)) / 2 33 | 34 | aX := math.Abs(float64(mid0X - mid1X)) 35 | aY := math.Abs(float64(mid0Y - mid1Y)) 36 | 37 | maX := math.Abs(float64((m0.Width + m1.Width) / 2)) 38 | maY := math.Abs(float64((m0.Height + m1.Height) / 2)) 39 | 40 | if aX < maX && aY < maY { 41 | return true 42 | } 43 | 44 | return false 45 | } 46 | 47 | func (m *Manager) bestMoveOffset(r0, r1 x.Rectangle) (int, int, error) { 48 | selfTopLeft := Point{X: int(r0.X), Y: int(r0.Y)} 49 | selfPoints := make([]Point, 0) 50 | selfPoints = append(selfPoints, selfTopLeft, Point{int(r0.X) + int(r0.Width), int(r0.Y)}, Point{int(r0.X), int(r0.Y) + int(r0.Height)}, Point{int(r0.X) + int(r0.Width), int(r0.Y) + int(r0.Height)}) 51 | 52 | otherTopLeft := Point{X: int(r1.X), Y: int(r1.Y)} 53 | otherPoints := make([]Point, 0) 54 | otherPoints = append(otherPoints, otherTopLeft, Point{int(r1.X) + int(r1.Width), int(r1.Y)}, Point{int(r1.X), int(r1.Y) + int(r1.Height)}, Point{int(r1.X) + int(r1.Width), int(r1.Y) + int(r1.Height)}) 55 | 56 | cb := func(x, y, target1, target2 int) bool { 57 | if (x == target1 && y == target2) || (x == target2 && y == target1) { 58 | return true 59 | } 60 | return false 61 | } 62 | 63 | var bestOffset Point 64 | needOffset := false 65 | min := INT_MAX 66 | for i, p1 := range selfPoints { 67 | for j, p2 := range otherPoints { 68 | offset := Point{ 69 | X: p1.X - p2.X, 70 | Y: p1.Y - p2.Y, 71 | } 72 | if m.mutiMonitorsPos == MonitorsLeftRight { 73 | if !cb(i, j, rectPoint0, rectPoint1) && !cb(i, j, rectPoint2, rectPoint3) { 74 | continue 75 | } 76 | } else if m.mutiMonitorsPos == MonitorsUpDown { 77 | if !cb(i, j, rectPoint0, rectPoint2) && !cb(i, j, rectPoint1, rectPoint3) { 78 | continue 79 | } 80 | } else if m.mutiMonitorsPos == MonitorsDiagonal { 81 | if !cb(i, j, rectPoint1, rectPoint2) && !cb(i, j, rectPoint0, rectPoint3) { 82 | continue 83 | } 84 | } else { 85 | 86 | } 87 | 88 | mt := int(math.Pow(float64(offset.X), 2) + math.Pow(float64(offset.Y), 2)) 89 | if mt >= min { 90 | continue 91 | } 92 | 93 | // test intersect 94 | rTemp := x.Rectangle{X: r0.X - int16(offset.X), Y: r0.Y - int16(offset.Y), Width: r0.Width, Height: r0.Height} 95 | if m.intersects(rTemp, r1) { 96 | continue 97 | } 98 | 99 | min = mt 100 | bestOffset = offset 101 | needOffset = true 102 | } 103 | } 104 | 105 | if !needOffset { 106 | return 0, 0, fmt.Errorf("no offset") 107 | } 108 | 109 | return bestOffset.X, bestOffset.Y, nil 110 | } 111 | 112 | func (m *Manager) bestMovePosition(r0, r1 x.Rectangle) (x.Rectangle, x.Rectangle, error) { 113 | logger.Debug("bestMovePosition before move primary monitor:", r0) 114 | logger.Debug("bestMovePositionr before move second monitor:", r1) 115 | offsetX, offsetY, err := m.bestMoveOffset(r0, r1) 116 | if err != nil { 117 | return x.Rectangle{}, x.Rectangle{}, err 118 | } 119 | 120 | //将r0和r1进行拼接,拼接后为rt0和rt1 121 | rt0 := x.Rectangle{ 122 | X: r0.X - int16(offsetX), 123 | Y: r0.Y - int16(offsetY), 124 | Width: r0.Width, 125 | Height: r0.Height, 126 | } 127 | 128 | rt1 := x.Rectangle{ 129 | X: r1.X, 130 | Y: r1.Y, 131 | Width: r1.Width, 132 | Height: r1.Height, 133 | } 134 | 135 | //计算合并后的左上角坐标 136 | minX := int(math.Min(float64(rt0.X), float64(rt1.X))) 137 | minY := int(math.Min(float64(rt0.Y), float64(rt1.Y))) 138 | 139 | //对两个矩形进行左上角坐标偏移计算 140 | rt0.X = rt0.X - int16(minX) 141 | rt0.Y = rt0.Y - int16(minY) 142 | 143 | rt1.X = rt1.X - int16(minX) 144 | rt1.Y = rt1.Y - int16(minY) 145 | 146 | logger.Debug("bestMovePosition after move primary monitor:", rt0) 147 | logger.Debug("bestMovePositionr after move second monitor:", rt1) 148 | 149 | return rt0, rt1, nil 150 | } 151 | -------------------------------------------------------------------------------- /xsettings/xsettings_reader.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package xsettings 6 | 7 | import ( 8 | "bytes" 9 | "encoding/binary" 10 | "fmt" 11 | "io" 12 | ) 13 | 14 | const ( 15 | settingTypeInteger uint8 = iota 16 | settingTypeString 17 | settingTypeColor 18 | ) 19 | 20 | var ( 21 | defaultByteOrder = binary.LittleEndian 22 | ) 23 | 24 | type stringValueInfo struct { 25 | length uint32 26 | value string 27 | } 28 | 29 | type integerValueInfo struct { 30 | value int32 31 | } 32 | 33 | type colorValueInfo struct { 34 | red uint16 35 | green uint16 36 | blue uint16 37 | //If the setting does not need the alpha field, 38 | //it should be set to 65535. 39 | alpha uint16 40 | } 41 | 42 | type xsItemHeader struct { 43 | sType uint8 // setting type 44 | nameLen uint16 // name length 45 | name string 46 | lastChangeSerial uint32 47 | } 48 | 49 | type xsItemInfo struct { 50 | header *xsItemHeader 51 | value interface{} 52 | } 53 | 54 | type xsItemInfos []xsItemInfo 55 | 56 | type xsDataInfo struct { 57 | byteOrder uint8 58 | serial uint32 59 | numSettings uint32 60 | 61 | items xsItemInfos 62 | } 63 | 64 | func (infos xsItemInfos) listProps() string { 65 | var content = "[" 66 | for i, info := range infos { 67 | if i != 0 { 68 | content += "," 69 | } 70 | content += fmt.Sprintf("%q", info.header.name) 71 | } 72 | return content + "]" 73 | } 74 | 75 | func (info *xsDataInfo) getPropItem(prop string) *xsItemInfo { 76 | for _, item := range info.items { 77 | if prop == item.header.name { 78 | return &item 79 | } 80 | } 81 | 82 | return nil 83 | } 84 | 85 | func unmarshalSettingData(data []byte) *xsDataInfo { 86 | var info xsDataInfo 87 | if len(data) == 0 { 88 | info.byteOrder = xsDataOrder 89 | info.numSettings = 0 90 | info.serial = xsDataSerial 91 | return &info 92 | } 93 | 94 | var reader = bytes.NewReader(data) 95 | 96 | readInteger(reader, &info.byteOrder) 97 | readSkip(reader, 3) 98 | readInteger(reader, &info.serial) 99 | readInteger(reader, &info.numSettings) 100 | for i := 0; i < int(info.numSettings); i++ { 101 | var item = xsItemInfo{ 102 | header: &xsItemHeader{}, 103 | } 104 | readXSItemInfo(reader, &item) 105 | info.items = append(info.items, item) 106 | } 107 | 108 | return &info 109 | } 110 | 111 | func readSkip(reader io.Reader, num int) { 112 | var buf = make([]byte, num) 113 | err := binary.Read(reader, defaultByteOrder, &buf) 114 | if err != nil { 115 | logger.Warning(err) 116 | } 117 | } 118 | 119 | func readInteger(reader io.Reader, v interface{}) { 120 | err := binary.Read(reader, defaultByteOrder, v) 121 | if err != nil { 122 | logger.Warning(err) 123 | } 124 | } 125 | 126 | func readString(reader io.Reader, v *string, length int) { 127 | var buf = make([]byte, length) 128 | err := binary.Read(reader, defaultByteOrder, &buf) 129 | if err != nil { 130 | logger.Warning(err) 131 | } 132 | *v = string(buf) 133 | } 134 | 135 | func readXSItemInfo(reader io.Reader, item *xsItemInfo) { 136 | readXSItemHeader(reader, item.header) 137 | 138 | switch item.header.sType { 139 | case settingTypeInteger: 140 | var v = integerValueInfo{} 141 | readXSValueInteger(reader, &v) 142 | item.value = &v 143 | case settingTypeString: 144 | var v = stringValueInfo{} 145 | readXSValueString(reader, &v) 146 | item.value = &v 147 | case settingTypeColor: 148 | var v = colorValueInfo{} 149 | readXSValueColor(reader, &v) 150 | item.value = &v 151 | } 152 | } 153 | 154 | func readXSItemHeader(reader io.Reader, header *xsItemHeader) { 155 | readInteger(reader, &header.sType) 156 | readSkip(reader, 1) 157 | readInteger(reader, &header.nameLen) 158 | readString(reader, &header.name, int(header.nameLen)) 159 | readSkip(reader, pad(int(header.nameLen))) 160 | readInteger(reader, &header.lastChangeSerial) 161 | } 162 | 163 | func readXSValueInteger(reader io.Reader, v *integerValueInfo) { 164 | readInteger(reader, &v.value) 165 | } 166 | 167 | func readXSValueString(reader io.Reader, v *stringValueInfo) { 168 | readInteger(reader, &v.length) 169 | readString(reader, &v.value, int(v.length)) 170 | readSkip(reader, pad(int(v.length))) 171 | } 172 | 173 | func readXSValueColor(reader io.Reader, v *colorValueInfo) { 174 | readInteger(reader, &v.red) 175 | readInteger(reader, &v.green) 176 | readInteger(reader, &v.blue) 177 | readInteger(reader, &v.alpha) 178 | } 179 | -------------------------------------------------------------------------------- /xsettings/xsettings_dpi.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package xsettings 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "path" 11 | "strconv" 12 | "strings" 13 | 14 | "github.com/linuxdeepin/go-lib/utils" 15 | ) 16 | 17 | const ( 18 | DPI_FALLBACK = 96 19 | HIDPI_LIMIT = DPI_FALLBACK * 2 20 | 21 | ffKeyPixels = `user_pref("layout.css.devPixelsPerPx",` 22 | ) 23 | 24 | // TODO: update 'antialias, hinting, hintstyle, rgba, cursor-theme, cursor-size' 25 | func (m *XSManager) updateDPI() { 26 | scale := m.cfgHelper.GetDouble(gsKeyScaleFactor) 27 | if scale <= 0 { 28 | scale = 1 29 | } 30 | 31 | var infos []xsSetting 32 | scaledDPI := int32(float64(DPI_FALLBACK*1024) * scale) 33 | if scaledDPI != m.cfgHelper.GetInt("xft-dpi") { 34 | m.cfgHelper.SetInt("xft-dpi", scaledDPI) 35 | infos = append(infos, xsSetting{ 36 | sType: settingTypeInteger, 37 | prop: "Xft/DPI", 38 | value: scaledDPI, 39 | }) 40 | } 41 | 42 | // update window scale and cursor size 43 | windowScale := m.cfgHelper.GetInt(gsKeyWindowScale) 44 | if windowScale > 1 { 45 | scaledDPI = int32(DPI_FALLBACK * 1024) 46 | } 47 | cursorSize := m.cfgHelper.GetInt(gsKeyGtkCursorThemeSize) 48 | v, _ := m.GetInteger("Gdk/WindowScalingFactor") 49 | if v != windowScale { 50 | infos = append(infos, xsSetting{ 51 | sType: settingTypeInteger, 52 | prop: "Gdk/WindowScalingFactor", 53 | value: windowScale, 54 | }, xsSetting{ 55 | sType: settingTypeInteger, 56 | prop: "Gdk/UnscaledDPI", 57 | value: scaledDPI, 58 | }, xsSetting{ 59 | sType: settingTypeInteger, 60 | prop: "Gtk/CursorThemeSize", 61 | value: cursorSize, 62 | }) 63 | } 64 | 65 | if len(infos) != 0 { 66 | err := m.setSettings(infos) 67 | if err != nil { 68 | logger.Warning("Failed to update dpi:", err) 69 | } 70 | m.updateXResources() 71 | } 72 | } 73 | 74 | func (m *XSManager) updateXResources() { 75 | scaleFactor := m.cfgHelper.GetDouble(gsKeyScaleFactor) 76 | xftDpi := int(DPI_FALLBACK * scaleFactor) 77 | updateXResources(xresourceInfos{ 78 | &xresourceInfo{ 79 | key: "Xcursor.theme", 80 | value: m.cfgHelper.GetString("gtk-cursor-theme-name"), 81 | }, 82 | &xresourceInfo{ 83 | key: "Xcursor.size", 84 | value: fmt.Sprintf("%d", m.cfgHelper.GetInt(gsKeyGtkCursorThemeSize)), 85 | }, 86 | &xresourceInfo{ 87 | key: "Xft.dpi", 88 | value: strconv.Itoa(xftDpi), 89 | }, 90 | }) 91 | } 92 | 93 | var ffDir = path.Join(os.Getenv("HOME"), ".mozilla/firefox") 94 | 95 | func (m *XSManager) updateFirefoxDPI() { 96 | scale := m.cfgHelper.GetDouble(gsKeyScaleFactor) 97 | if scale <= 0 { 98 | // firefox default value: -1 99 | scale = -1 100 | } 101 | 102 | configs, err := getFirefoxConfigs(ffDir) 103 | if err != nil { 104 | logger.Debug("Failed to get firefox configs:", err) 105 | return 106 | } 107 | 108 | for _, config := range configs { 109 | err = setFirefoxDPI(scale, config, config) 110 | if err != nil { 111 | logger.Warning("Failed to set firefox dpi:", config, err) 112 | } 113 | } 114 | } 115 | 116 | func getFirefoxConfigs(dir string) ([]string, error) { 117 | finfos, err := os.ReadDir(dir) 118 | if err != nil { 119 | return nil, err 120 | } 121 | var configs []string 122 | for _, finfo := range finfos { 123 | config := path.Join(dir, finfo.Name(), "prefs.js") 124 | if !utils.IsFileExist(config) { 125 | continue 126 | } 127 | configs = append(configs, config) 128 | } 129 | return configs, nil 130 | } 131 | 132 | func setFirefoxDPI(value float64, src, dest string) error { 133 | contents, err := os.ReadFile(src) 134 | if err != nil { 135 | return err 136 | } 137 | lines := strings.Split(string(contents), "\n") 138 | target := fmt.Sprintf("%s \"%.2f\");", ffKeyPixels, value) 139 | found := false 140 | for i, line := range lines { 141 | if line == "" || line[0] == '#' { 142 | continue 143 | } 144 | if !strings.Contains(line, ffKeyPixels) { 145 | continue 146 | } 147 | 148 | if line == target { 149 | return nil 150 | } 151 | 152 | tmp := strings.Split(ffKeyPixels, ",")[0] + ", " + 153 | fmt.Sprintf("\"%.2f\");", value) 154 | lines[i] = tmp 155 | found = true 156 | break 157 | } 158 | if !found { 159 | if value == -1 { 160 | return nil 161 | } 162 | tmp := lines[len(lines)-1] 163 | lines[len(lines)-1] = target 164 | lines = append(lines, tmp) 165 | } 166 | return os.WriteFile(dest, []byte(strings.Join(lines, "\n")), 0644) 167 | } 168 | -------------------------------------------------------------------------------- /display/config_v4.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "encoding/json" 9 | "os" 10 | ) 11 | 12 | type ConfigV4 map[string]*ScreenConfigV4 13 | 14 | type ScreenConfigV4 struct { 15 | Custom []*CustomModeConfig `json:",omitempty"` 16 | Mirror *MirrorModeConfig `json:",omitempty"` 17 | Extend *ExtendModeConfig `json:",omitempty"` 18 | OnlyOne *OnlyOneModeConfig `json:",omitempty"` 19 | Single *MonitorConfigV5 `json:",omitempty"` 20 | } 21 | 22 | type CustomModeConfig struct { 23 | Name string 24 | Monitors []*MonitorConfigV5 25 | } 26 | 27 | type MirrorModeConfig struct { 28 | Monitors []*MonitorConfigV5 29 | } 30 | 31 | type ExtendModeConfig struct { 32 | Monitors []*MonitorConfigV5 33 | } 34 | 35 | type OnlyOneModeConfig struct { 36 | Monitors []*MonitorConfigV5 37 | } 38 | 39 | func loadConfigV4(filename string) (ConfigV4, error) { 40 | // #nosec G304 41 | data, err := os.ReadFile(filename) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | var c ConfigV4 47 | err = json.Unmarshal(data, &c) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | return c, nil 53 | } 54 | 55 | // 需要 Manager 的 Brightness,gsColorTemperatureMode, gsColorTemperatureManual 56 | // 还会影响 Manager 的 DisplayMode 57 | func (c ConfigV4) toConfig(m *Manager) ConfigV5 { 58 | newConfig := make(ConfigV5) 59 | 60 | for id, sc := range c { 61 | cfgKey := parseConfigKey(id) 62 | jId := cfgKey.getJoinedId() 63 | // 单屏幕,可设置分辨率 64 | if len(cfgKey.idFields) == 1 { 65 | //配置文件中保存的可能为空值 66 | if sc.Single != nil { 67 | //把亮度,色温写入配置文件 68 | sc.Single.Brightness = m.Brightness[sc.Single.Name] 69 | newConfig[jId] = &ScreenConfigV5{ 70 | Mirror: nil, 71 | Extend: nil, 72 | OnlyOne: nil, 73 | Single: &SingleModeConfigV5{ 74 | Monitor: sc.Single, 75 | ColorTemperatureMode: m.gsColorTemperatureMode, 76 | ColorTemperatureManual: m.gsColorTemperatureManual, 77 | }, 78 | } 79 | } 80 | } else { 81 | screenCfg := newConfig[id] 82 | if screenCfg == nil { 83 | screenCfg = &ScreenConfigV5{} 84 | newConfig[id] = screenCfg 85 | } 86 | sc.toModeConfigs(screenCfg, m) 87 | } 88 | } 89 | return newConfig 90 | } 91 | 92 | func (sc *ScreenConfigV4) toModeConfigs(screenCfg *ScreenConfigV5, m *Manager) { 93 | if sc.OnlyOne != nil { 94 | result := make([]*MonitorConfigV5, 0, len(sc.OnlyOne.Monitors)) 95 | for _, monitor := range sc.OnlyOne.Monitors { 96 | monitor.Brightness = m.Brightness[monitor.Name] 97 | result = append(result, monitor) 98 | } 99 | screenCfg.setModeConfigs(DisplayModeOnlyOne, m.gsColorTemperatureMode, m.gsColorTemperatureManual, result) 100 | } 101 | 102 | //默认自定义数据,自定义没数据就用复制 扩展的数据 103 | if len(sc.Custom) != 0 { 104 | // 直接取最后一个 105 | custom := sc.Custom[len(sc.Custom)-1] 106 | result := make([]*MonitorConfigV5, 0, len(custom.Monitors)) 107 | for _, monitor := range custom.Monitors { 108 | monitor.Brightness = m.Brightness[monitor.Name] 109 | result = append(result, monitor) 110 | } 111 | 112 | if result[0].X == result[1].X { 113 | screenCfg.setModeConfigs(DisplayModeMirror, m.gsColorTemperatureMode, m.gsColorTemperatureManual, result) 114 | //如果升级之前是自定义模式.重新判断是拆分/合并模式 115 | if m.DisplayMode == DisplayModeCustom { 116 | m.setDisplayMode(DisplayModeMirror) 117 | } 118 | } else { 119 | screenCfg.setModeConfigs(DisplayModeExtend, m.gsColorTemperatureMode, m.gsColorTemperatureManual, result) 120 | //如果升级之前是自定义模式.重新判断是拆分/合并模式 121 | if m.DisplayMode == DisplayModeCustom { 122 | m.setDisplayMode(DisplayModeExtend) 123 | } 124 | } 125 | return 126 | } else { 127 | if m.DisplayMode == DisplayModeCustom { 128 | m.setDisplayMode(DisplayModeMirror) 129 | } 130 | } 131 | 132 | if sc.Mirror != nil { 133 | result := make([]*MonitorConfigV5, 0, len(sc.Mirror.Monitors)) 134 | for _, monitor := range sc.Mirror.Monitors { 135 | monitor.Brightness = m.Brightness[monitor.Name] 136 | result = append(result, monitor) 137 | } 138 | screenCfg.setModeConfigs(DisplayModeMirror, m.gsColorTemperatureMode, m.gsColorTemperatureManual, result) 139 | } 140 | 141 | if sc.Extend != nil { 142 | result := make([]*MonitorConfigV5, 0, len(sc.Extend.Monitors)) 143 | for _, monitor := range sc.Extend.Monitors { 144 | monitor.Brightness = m.Brightness[monitor.Name] 145 | result = append(result, monitor) 146 | } 147 | screenCfg.setModeConfigs(DisplayModeExtend, m.gsColorTemperatureMode, m.gsColorTemperatureManual, result) 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /xsettings/utils_dconfig.go: -------------------------------------------------------------------------------- 1 | package xsettings 2 | 3 | import ( 4 | "github.com/godbus/dbus/v5" 5 | configManager "github.com/linuxdeepin/go-dbus-factory/org.desktopspec.ConfigManager" 6 | "github.com/linuxdeepin/go-lib/dbusutil" 7 | "github.com/linuxdeepin/go-lib/strv" 8 | ) 9 | 10 | type DConfig struct { 11 | conn *dbus.Conn 12 | c configManager.Manager 13 | gs *GSConfig //for sync 14 | keyList []string 15 | } 16 | 17 | func NewDSConfig(conn *dbus.Conn) *DConfig { 18 | dsg := configManager.NewConfigManager(conn) 19 | XSettingsConfigManagerPath, err := dsg.AcquireManager(0, dsettingsAppID, dsettingsXSettingsName, "") 20 | if err != nil { 21 | logger.Warning(err) 22 | return nil 23 | } 24 | dConfigManager, err := configManager.NewManager(conn, XSettingsConfigManagerPath) 25 | if err != nil { 26 | logger.Warning(err) 27 | return nil 28 | } 29 | keyList, _ := dConfigManager.KeyList().Get(0) 30 | return &DConfig{ 31 | c: dConfigManager, 32 | conn: conn, 33 | keyList: keyList, 34 | gs: NewGSConfig(), 35 | } 36 | } 37 | 38 | func (d *DConfig) ListKeys() []string { 39 | return d.keyList 40 | } 41 | 42 | func (d *DConfig) hasKey(key string) bool { 43 | if !strv.Strv(d.keyList).Contains(key) { 44 | logger.Warningf("key %v not found in dconfig", key) 45 | return false 46 | } 47 | return true 48 | } 49 | 50 | func (d *DConfig) GetString(key string) string { 51 | if !d.hasKey(key) { 52 | return "" 53 | } 54 | value, err := d.c.Value(0, key) 55 | if err != nil { 56 | logger.Warning(err) 57 | return "" 58 | } 59 | v, ok := value.Value().(string) 60 | if !ok { 61 | logger.Warningf("key %v type is not string, real is:%T", key, value.Value()) 62 | return "" 63 | } 64 | return v 65 | } 66 | 67 | func (d *DConfig) GetInt(key string) int32 { 68 | if !d.hasKey(key) { 69 | return -1 70 | } 71 | value, err := d.c.Value(0, key) 72 | if err != nil { 73 | logger.Warning(err) 74 | return -1 75 | } 76 | v, ok := value.Value().(int64) 77 | if !ok { 78 | logger.Warningf("key %v type is not int, real is:%T", key, value.Value()) 79 | return -1 80 | } 81 | return int32(v) 82 | } 83 | 84 | func (d *DConfig) GetBoolean(key string) bool { 85 | if !d.hasKey(key) { 86 | return false 87 | } 88 | value, err := d.c.Value(0, key) 89 | if err != nil { 90 | logger.Warning(err) 91 | return false 92 | } 93 | v, ok := value.Value().(bool) 94 | if !ok { 95 | logger.Warningf("key %v type is not bool, real is:%T", key, value.Value()) 96 | return false 97 | } 98 | return v 99 | } 100 | 101 | func (d *DConfig) GetDouble(key string) float64 { 102 | if !d.hasKey(key) { 103 | return -1 104 | } 105 | value, err := d.c.Value(0, key) 106 | if err != nil { 107 | logger.Warning(err) 108 | return -1 109 | } 110 | v, ok := value.Value().(float64) 111 | if !ok { 112 | // TODO: dconfig float64的类型处理有问题,待该问题解决后去掉该代码 113 | logger.Warningf("key %v type is not float64, real is:%T", key, value.Value()) 114 | v1, ok := value.Value().(int64) 115 | if !ok { 116 | logger.Warningf("key %v type is not int64, real is:%T", key, value.Value()) 117 | return -1 118 | } 119 | v = float64(v1) 120 | } 121 | return v 122 | } 123 | 124 | func (d *DConfig) SetString(key string, value string) bool { 125 | if !d.hasKey(key) { 126 | return false 127 | } 128 | if err := d.c.SetValue(0, key, dbus.MakeVariant(value)); err != nil { 129 | logger.Warning(err) 130 | return false 131 | } 132 | if d.gs != nil { 133 | d.gs.SetString(key, value) 134 | } 135 | return true 136 | } 137 | 138 | func (d *DConfig) SetInt(key string, value int32) bool { 139 | if !d.hasKey(key) { 140 | return false 141 | } 142 | if err := d.c.SetValue(0, key, dbus.MakeVariant(value)); err != nil { 143 | logger.Warning(err) 144 | return false 145 | } 146 | if d.gs != nil { 147 | d.gs.SetInt(key, value) 148 | } 149 | return true 150 | } 151 | 152 | func (d *DConfig) SetBoolean(key string, value bool) bool { 153 | if !d.hasKey(key) { 154 | return false 155 | } 156 | if err := d.c.SetValue(0, key, dbus.MakeVariant(value)); err != nil { 157 | logger.Warning(err) 158 | return false 159 | } 160 | if d.gs != nil { 161 | d.gs.SetBoolean(key, value) 162 | } 163 | return true 164 | } 165 | 166 | func (d *DConfig) SetDouble(key string, value float64) bool { 167 | if !d.hasKey(key) { 168 | return false 169 | } 170 | if err := d.c.SetValue(0, key, dbus.MakeVariant(value)); err != nil { 171 | logger.Warning(err) 172 | return false 173 | } 174 | if d.gs != nil { 175 | d.gs.SetDouble(key, value) 176 | } 177 | return true 178 | } 179 | 180 | func (d *DConfig) HandleConfigChanged(cb func(string)) { 181 | systemSigLoop := dbusutil.NewSignalLoop(d.conn, 10) 182 | systemSigLoop.Start() 183 | d.c.InitSignalExt(systemSigLoop, true) 184 | _, _ = d.c.ConnectValueChanged(cb) 185 | } 186 | -------------------------------------------------------------------------------- /wl_display/handle_event.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | //func (m *Manager) listenEvent() { 8 | //eventChan := make(chan x.GenericEvent, 100) 9 | //m.xConn.AddEventChan(eventChan) 10 | // 11 | //root := m.xConn.GetDefaultScreen().Root 12 | //err := randr.SelectInputChecked(m.xConn, root, 13 | // randr.NotifyMaskOutputChange|randr.NotifyMaskOutputProperty| 14 | // randr.NotifyMaskCrtcChange|randr.NotifyMaskScreenChange).Check(m.xConn) 15 | //if err != nil { 16 | // logger.Warning("failed to select randr event:", err) 17 | // return 18 | //} 19 | // 20 | //rrExtData := m.xConn.GetExtensionData(randr.Ext()) 21 | // 22 | //go func() { 23 | // for ev := range eventChan { 24 | // switch ev.GetEventCode() { 25 | // case randr.NotifyEventCode + rrExtData.FirstEvent: 26 | // event, _ := randr.NewNotifyEvent(ev) 27 | // switch event.SubCode { 28 | // case randr.NotifyCrtcChange: 29 | // e, _ := event.NewCrtcChangeNotifyEvent() 30 | // m.handleCrtcChanged(e) 31 | // 32 | // case randr.NotifyOutputChange: 33 | // e, _ := event.NewOutputChangeNotifyEvent() 34 | // m.handleOutputChanged(e) 35 | // 36 | // case randr.NotifyOutputProperty: 37 | // e, _ := event.NewOutputPropertyNotifyEvent() 38 | // m.handleOutputPropertyChanged(e) 39 | // } 40 | // 41 | // case randr.ScreenChangeNotifyEventCode + rrExtData.FirstEvent: 42 | // e, _ := randr.NewScreenChangeNotifyEvent(ev) 43 | // m.handleScreenChanged(e) 44 | // } 45 | // } 46 | //}() 47 | //} 48 | 49 | //func (m *Manager) handleOutputChanged(ev *randr.OutputChangeNotifyEvent) { 50 | // logger.Debug("output changed", ev.Output) 51 | // 52 | // outputInfo, err := m.updateOutputInfo(ev.Output) 53 | // if err != nil { 54 | // logger.Warning(err) 55 | // } 56 | // 57 | // if outputInfo.Connection != randr.ConnectionConnected && 58 | // outputInfo.Name == m.Primary { 59 | // 60 | // for output0, outputInfo0 := range m.outputMap { 61 | // if outputInfo0.Connection == randr.ConnectionConnected { 62 | // // set first connected output as primary 63 | // err = m.setOutputPrimary(output0) 64 | // if err != nil { 65 | // logger.Warning(err) 66 | // } 67 | // break 68 | // } 69 | // } 70 | // } 71 | // 72 | // m.updateMonitor(ev.Output, outputInfo) 73 | // m.updatePropMonitors() 74 | // 75 | // oldMonitorsId := m.monitorsId 76 | // newMonitorsId := getMonitorsId(m.monitorMap) 77 | // if newMonitorsId != oldMonitorsId { 78 | // logger.Debug("new monitors id:", newMonitorsId) 79 | // m.markClean() 80 | // m.applyDisplayMode() 81 | // m.monitorsId = newMonitorsId 82 | // } 83 | //} 84 | // 85 | //func (m *Manager) handleOutputPropertyChanged(ev *randr.OutputPropertyNotifyEvent) { 86 | // logger.Debug("output property changed", ev.Output, ev.Atom) 87 | //} 88 | // 89 | //func (m *Manager) handleCrtcChanged(ev *randr.CrtcChangeNotifyEvent) { 90 | // logger.Debug("crtc changed", ev.Crtc) 91 | // crtcInfo, err := m.updateCrtcInfo(ev.Crtc) 92 | // if err != nil { 93 | // logger.Warning(err) 94 | // return 95 | // } 96 | // 97 | // var rOutput randr.Output 98 | // var rOutputInfo *randr.GetOutputInfoReply 99 | // 100 | // m.outputMapMu.Lock() 101 | // for output, outputInfo := range m.outputMap { 102 | // if outputInfo.Crtc == ev.Crtc { 103 | // rOutput = output 104 | // rOutputInfo = outputInfo 105 | // break 106 | // } 107 | // } 108 | // m.outputMapMu.Unlock() 109 | // 110 | // if rOutputInfo != nil { 111 | // m.PropsMu.Lock() 112 | // if m.Primary == rOutputInfo.Name { 113 | // m.setPropPrimaryRect(getCrtcRect(crtcInfo)) 114 | // } 115 | // m.PropsMu.Unlock() 116 | // } 117 | // 118 | // if rOutput != 0 { 119 | // m.outputMapMu.Lock() 120 | // monitor := m.monitorMap[rOutput] 121 | // m.outputMapMu.Unlock() 122 | // if monitor != nil { 123 | // logger.Debug("update monitor crtc", monitor.ID, monitor.Name) 124 | // m.updateMonitorCrtcInfo(monitor, crtcInfo) 125 | // } 126 | // } 127 | //} 128 | // 129 | //func (m *Manager) handleScreenChanged(ev *randr.ScreenChangeNotifyEvent) { 130 | // logger.Debugf("screen changed cfgTs: %v, screen size: %vx%v ", ev.ConfigTimestamp, 131 | // ev.Width, ev.Height) 132 | // 133 | // m.PropsMu.Lock() 134 | // m.setPropScreenWidth(ev.Width) 135 | // m.setPropScreenHeight(ev.Height) 136 | // cfgTsChanged := false 137 | // if m.configTimestamp != ev.ConfigTimestamp { 138 | // m.configTimestamp = ev.ConfigTimestamp 139 | // cfgTsChanged = true 140 | // } 141 | // m.PropsMu.Unlock() 142 | // 143 | // if cfgTsChanged { 144 | // logger.Debug("config timestamp changed") 145 | // resources, err := m.getScreenResourcesCurrent() 146 | // if err != nil { 147 | // logger.Warning("failed to get screen resources:", err) 148 | // } 149 | // m.modes = resources.Modes 150 | // } 151 | // 152 | // m.updateOutputPrimary() 153 | //} 154 | -------------------------------------------------------------------------------- /display/display_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func Test_getMaxAreaSize(t *testing.T) { 14 | size := getMaxAreaSize([]Size{ 15 | {1024, 768}, 16 | {640, 480}, 17 | {1280, 720}, 18 | {800, 600}, 19 | }) 20 | assert.Equal(t, Size{1280, 720}, size) 21 | size = getMaxAreaSize(nil) 22 | assert.Equal(t, Size{}, size) 23 | size = getMaxAreaSize([]Size{ 24 | {1024, 768}, 25 | }) 26 | assert.Equal(t, Size{1024, 768}, size) 27 | } 28 | 29 | func Test_filterModeInfos(t *testing.T) { 30 | modes := []ModeInfo{ 31 | { 32 | Id: 1, 33 | name: "1024x768", 34 | Width: 1024, 35 | Height: 768, 36 | Rate: 60.1, 37 | }, 38 | { 39 | Id: 2, 40 | name: "1024x768i", 41 | Width: 1024, 42 | Height: 768, 43 | Rate: 60.1, 44 | }, 45 | { 46 | Id: 3, 47 | name: "1024x768i", 48 | Width: 1024, 49 | Height: 768, 50 | Rate: 60.3, 51 | }, 52 | } 53 | assert.Equal(t, []ModeInfo{ 54 | { 55 | Id: 1, 56 | name: "1024x768", 57 | Width: 1024, 58 | Height: 768, 59 | Rate: 60.1, 60 | }, 61 | }, filterModeInfos(modes, ModeInfo{})) 62 | 63 | // -------------------------- 64 | modes = []ModeInfo{ 65 | { 66 | Id: 1, 67 | name: "1024x768", 68 | Width: 1024, 69 | Height: 768, 70 | Rate: 60.1, 71 | }, 72 | { 73 | Id: 2, 74 | name: "1024x768i", 75 | Width: 1024, 76 | Height: 768, 77 | Rate: 60.1, 78 | }, 79 | } 80 | assert.Equal(t, modes, filterModeInfos(modes, 81 | ModeInfo{Id: 2, 82 | name: "1024x768i", 83 | Width: 1024, 84 | Height: 768, 85 | Rate: 60.1, 86 | })) 87 | 88 | // -------------------------- 89 | modes = []ModeInfo{ 90 | { 91 | Id: 1, 92 | name: "1024x768", 93 | Width: 1024, 94 | Height: 768, 95 | Rate: 60.1, 96 | }, 97 | { 98 | Id: 2, 99 | name: "1024x768", 100 | Width: 1024, 101 | Height: 768, 102 | Rate: 60.10000001, 103 | }, 104 | { 105 | Id: 3, 106 | name: "1024x768", 107 | Width: 1024, 108 | Height: 768, 109 | Rate: 60.3, 110 | }, 111 | } 112 | assert.Equal(t, []ModeInfo{ 113 | { 114 | Id: 1, 115 | name: "1024x768", 116 | Width: 1024, 117 | Height: 768, 118 | Rate: 60.1, 119 | }, 120 | { 121 | Id: 3, 122 | name: "1024x768", 123 | Width: 1024, 124 | Height: 768, 125 | Rate: 60.3, 126 | }, 127 | }, filterModeInfos(modes, ModeInfo{})) 128 | 129 | // -------------------------- 130 | // 混合 131 | modes = []ModeInfo{ 132 | { 133 | Id: 1, 134 | name: "1024x768", 135 | Width: 1024, 136 | Height: 768, 137 | Rate: 60.1, 138 | }, 139 | { 140 | Id: 2, 141 | name: "1024x768", 142 | Width: 1024, 143 | Height: 768, 144 | Rate: 60.10000001, 145 | }, 146 | { 147 | Id: 3, 148 | name: "1024x768", 149 | Width: 1024, 150 | Height: 768, 151 | Rate: 60.3, 152 | }, 153 | { 154 | Id: 4, 155 | name: "1024x768i", 156 | Width: 1024, 157 | Height: 768, 158 | Rate: 60.1, 159 | }, 160 | { 161 | Id: 5, 162 | name: "1024x768i", 163 | Width: 1024, 164 | Height: 768, 165 | Rate: 60.3, 166 | }, 167 | } 168 | assert.Equal(t, []ModeInfo{ 169 | { 170 | Id: 1, 171 | name: "1024x768", 172 | Width: 1024, 173 | Height: 768, 174 | Rate: 60.1, 175 | }, 176 | { 177 | Id: 3, 178 | name: "1024x768", 179 | Width: 1024, 180 | Height: 768, 181 | Rate: 60.3, 182 | }, 183 | }, filterModeInfos(modes, ModeInfo{})) 184 | } 185 | 186 | func TestCalcRecommendedScaleFactor(t *testing.T) { 187 | for _, rec := range []struct { 188 | widthPx float64 189 | heightPx float64 190 | widthMm float64 191 | heightMm float64 192 | expect float64 193 | }{ 194 | {1366, 768, 310, 147, 1}, 195 | {1366, 768, 277, 165, 1}, 196 | {1366, 768, 309, 174, 1}, 197 | 198 | {1600, 900, 294, 166, 1}, 199 | 200 | {1920, 1080, 344, 194, 1.25}, 201 | {1920, 1080, 477, 268, 1}, 202 | {1920, 1080, 527, 296, 1}, 203 | {1920, 1080, 476, 268, 1}, 204 | {1920, 1080, 520, 310, 1}, 205 | {1920, 1080, 708, 398, 1}, 206 | {1920, 1080, 518, 324, 1}, 207 | {1920, 1080, 510, 287, 1}, 208 | {1920, 1080, 527, 296, 1}, 209 | {1920, 1080, 309, 174, 1.25}, 210 | {1920, 1080, 293, 165, 1.25}, 211 | {1920, 1080, 294, 165, 1.25}, 212 | 213 | {2160, 1440, 280, 180, 1.5}, 214 | 215 | {3000, 2000, 290, 200, 2}, 216 | 217 | {3840, 2160, 600, 340, 2}, 218 | {3840, 2160, 344, 193, 2.25}, 219 | } { 220 | factor := calcRecommendedScaleFactor(rec.widthPx, rec.heightPx, rec.widthMm, rec.heightMm) 221 | assert.Equal(t, rec.expect, factor, "%gx%g %gmm x %gmm", 222 | rec.widthPx, rec.heightPx, rec.widthMm, rec.heightMm) 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /wl_display/wl.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | //func (m *Manager) loopDispatch() { 8 | // for { 9 | // select { 10 | // case m.display.Context().Dispatch() <- struct{}{}: 11 | // } 12 | // } 13 | //} 14 | 15 | //type registryGlobalHandler struct { 16 | // ch chan wl.RegistryGlobalEvent 17 | //} 18 | // 19 | //func (rgh registryGlobalHandler) HandleRegistryGlobal(ev wl.RegistryGlobalEvent) { 20 | // rgh.ch <- ev 21 | //} 22 | // 23 | //type callbackDoneHandler struct { 24 | // ch chan wl.CallbackDoneEvent 25 | // cb *wl.Callback 26 | //} 27 | // 28 | //func (cbh *callbackDoneHandler) Chan() <-chan wl.CallbackDoneEvent { 29 | // return cbh.ch 30 | //} 31 | // 32 | //func (cdh *callbackDoneHandler) Remove() { 33 | // cdh.cb.RemoveDoneHandler(cdh) 34 | //} 35 | // 36 | //func (cdh *callbackDoneHandler) HandleCallbackDone(ev wl.CallbackDoneEvent) { 37 | // cdh.ch <- ev 38 | //} 39 | // 40 | //type outputCfgAppliedHandler struct { 41 | // ch chan output_management.OutputconfigurationAppliedEvent 42 | //} 43 | // 44 | //func (h outputCfgAppliedHandler) HandleOutputconfigurationApplied(ev output_management.OutputconfigurationAppliedEvent) { 45 | // h.ch <- ev 46 | //} 47 | // 48 | //type outputCfgFailedHandler struct { 49 | // ch chan output_management.OutputconfigurationFailedEvent 50 | //} 51 | // 52 | //func (h outputCfgFailedHandler) HandleOutputconfigurationFailed(ev output_management.OutputconfigurationFailedEvent) { 53 | // h.ch <- ev 54 | //} 55 | // 56 | //func doSync(display *wl.Display) (*callbackDoneHandler, error) { 57 | // callback, err := display.Sync() 58 | // if err != nil { 59 | // return nil, err 60 | // } 61 | // cbdChan := make(chan wl.CallbackDoneEvent) 62 | // cbdHandler := &callbackDoneHandler{ch: cbdChan, cb: callback} 63 | // callback.AddDoneHandler(cbdHandler) 64 | // return cbdHandler, nil 65 | //} 66 | // 67 | //func (m *Manager) registerGlobals() error { 68 | // registry, err := m.display.GetRegistry() 69 | // if err != nil { 70 | // return err 71 | // } 72 | // m.registry = registry 73 | // 74 | // rgeChan := make(chan wl.RegistryGlobalEvent) 75 | // rgeHandler := registryGlobalHandler{ch: rgeChan} 76 | // registry.AddGlobalHandler(rgeHandler) 77 | // 78 | // cbdHandler, err := doSync(m.display) 79 | // if err != nil { 80 | // return err 81 | // } 82 | // 83 | //loop: 84 | // for { 85 | // select { 86 | // case ev := <-rgeChan: 87 | // //logger.Debugf("ev: %#v\n", ev) 88 | // err = m.registerInterface(ev) 89 | // if err != nil { 90 | // logger.Warning(err) 91 | // } 92 | // case m.display.Context().Dispatch() <- struct{}{}: 93 | // case <-cbdHandler.Chan(): 94 | // break loop 95 | // } 96 | // } 97 | // 98 | // registry.RemoveGlobalHandler(rgeHandler) 99 | // cbdHandler.Remove() 100 | // 101 | // registry.AddGlobalHandler(m) 102 | // registry.AddGlobalRemoveHandler(m) 103 | // return nil 104 | //} 105 | 106 | //func (m *Manager) HandleRegistryGlobal(ev wl.RegistryGlobalEvent) { 107 | // // handle monitor add 108 | // switch ev.Interface { 109 | // case "org_kde_kwin_outputdevice": 110 | // err := m.registerOutputDevice(ev) 111 | // if err != nil { 112 | // logger.Warningf("failed to register output device %v: %v", ev.Name, err) 113 | // } 114 | // } 115 | //} 116 | // 117 | //func (m *Manager) HandleRegistryGlobalRemove(ev wl.RegistryGlobalRemoveEvent) { 118 | // // handle monitor remove 119 | // for id, monitor := range m.monitorMap { 120 | // if ev.Name == monitor.device.regName { 121 | // m.removeMonitor(id) 122 | // m.updatePropMonitors() 123 | // m.updateMonitorsId() 124 | // m.updateScreenSize() 125 | // return 126 | // } 127 | // } 128 | //} 129 | 130 | //func (m *Manager) registerOutputDevice(ev wl.RegistryGlobalEvent) error { 131 | // device := outputdevice.NewOutputdevice(m.display.Context()) 132 | // logger.Debug("register output device", device.Id()) 133 | // err := m.registry.Bind(ev.Name, ev.Interface, ev.Version, device) 134 | // if err != nil { 135 | // return err 136 | // } 137 | // odh := newOutputDeviceHandler(device, ev.Name) 138 | // odh.doneCb = m.handleOutputDeviceDone 139 | // odh.enabledCb = m.handleOutputDeviceEnabled 140 | // return nil 141 | //} 142 | // 143 | //func (m *Manager) registerInterface(ev wl.RegistryGlobalEvent) error { 144 | // switch ev.Interface { 145 | // 146 | // case "org_kde_kwin_outputdevice": 147 | // err := m.registerOutputDevice(ev) 148 | // if err != nil { 149 | // return err 150 | // } 151 | // 152 | // m.devicesAllDoneMu.Lock() 153 | // if !m.devicesAllDone { 154 | // m.devicesWg.Add(1) 155 | // } 156 | // m.devicesAllDoneMu.Unlock() 157 | // 158 | // return nil 159 | // 160 | // case "org_kde_kwin_outputmanagement": 161 | // outputManagement := output_management.NewOutputmanagement(m.display.Context()) 162 | // logger.Debug("register output management", outputManagement.Id()) 163 | // err := m.registry.Bind(ev.Name, ev.Interface, ev.Version, outputManagement) 164 | // if err != nil { 165 | // return err 166 | // } 167 | // 168 | // m.management = outputManagement 169 | // } 170 | // return nil 171 | //} 172 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2014 - 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package main 6 | 7 | import "C" 8 | import ( 9 | "flag" 10 | "os" 11 | "os/exec" 12 | "path/filepath" 13 | "strings" 14 | "syscall" 15 | "time" 16 | 17 | dbus "github.com/godbus/dbus/v5" 18 | x "github.com/linuxdeepin/go-x11-client" 19 | "github.com/linuxdeepin/startdde/display" 20 | 21 | // "github.com/linuxdeepin/startdde/watchdog" 22 | "github.com/linuxdeepin/go-lib/dbusutil" 23 | "github.com/linuxdeepin/go-lib/gettext" 24 | "github.com/linuxdeepin/go-lib/gsettings" 25 | "github.com/linuxdeepin/go-lib/log" 26 | wl_display "github.com/linuxdeepin/startdde/wl_display" 27 | "github.com/linuxdeepin/startdde/xsettings" 28 | ) 29 | 30 | var logger = log.NewLogger("startdde") 31 | 32 | var globalCgExecBin string 33 | 34 | var globalXSManager *xsettings.XSManager 35 | 36 | var _xConn *x.Conn 37 | 38 | var _useWayland bool 39 | 40 | var _inVM bool 41 | 42 | var _useKWin bool 43 | 44 | func init() { 45 | } 46 | 47 | func reapZombies() { 48 | // We must reap children process even we hasn't create anyone at this moment, 49 | // Because the startdde may be launched by exec syscall 50 | // in another existed process, like /usr/sbin/lighdm-session does. 51 | // NOTE: Don't use signal.Ignore(syscall.SIGCHILD), otherwise os/exec wouldn't work properly. 52 | // And simply ignore SIGCHILD hasn't any helpful in here. 53 | for { 54 | pid, err := syscall.Wait4(-1, nil, syscall.WNOHANG, nil) 55 | if err != nil || pid == 0 { 56 | break 57 | } 58 | } 59 | } 60 | 61 | var _mainBeginTime time.Time 62 | 63 | func logDebugAfter(msg string) { 64 | elapsed := time.Since(_mainBeginTime) 65 | logger.Debugf("after %s, %s", elapsed, msg) 66 | } 67 | 68 | func logInfoAfter(msg string) { 69 | elapsed := time.Since(_mainBeginTime) 70 | logger.Infof("after %s, %s", elapsed, msg) 71 | } 72 | 73 | func greeterDisplayMain() { 74 | display.SetGreeterMode(true) 75 | // init x conn 76 | xConn, err := x.NewConn() 77 | if err != nil { 78 | logger.Warning(err) 79 | os.Exit(1) 80 | } 81 | // TODO 82 | display.Init(xConn, false, false) 83 | logger.Debug("greeter mode") 84 | service, err := dbusutil.NewSessionService() 85 | if err != nil { 86 | logger.Warning(err) 87 | } 88 | err = display.Start(service) 89 | if err != nil { 90 | logger.Warning(err) 91 | } 92 | err = display.StartPart2() 93 | if err != nil { 94 | logger.Warning(err) 95 | } 96 | service.Wait() 97 | } 98 | 99 | func main() { 100 | flag.Parse() 101 | if len(os.Args) > 0 && strings.HasPrefix(filepath.Base(os.Args[0]), "greeter") { 102 | // os.Args[0] 应该一般是 greeter-display-daemon 103 | greeterDisplayMain() 104 | return 105 | } 106 | 107 | _mainBeginTime = time.Now() 108 | 109 | gettext.InitI18n() 110 | gettext.BindTextdomainCodeset("startdde", "UTF-8") 111 | gettext.Textdomain("startdde") 112 | 113 | reapZombies() 114 | // init x conn 115 | xConn, err := x.NewConn() 116 | if err != nil { 117 | logger.Warning(err) 118 | os.Exit(1) 119 | } 120 | _xConn = xConn 121 | var recommendedScaleFactor float64 122 | if os.Getenv("WAYLAND_DISPLAY") != "" { 123 | logger.Info("in wayland mode") 124 | _useWayland = true 125 | } 126 | 127 | _inVM, err = isInVM() 128 | 129 | display.Init(xConn, _useWayland, _inVM) 130 | // TODO 131 | recommendedScaleFactor = display.GetRecommendedScaleFactor() 132 | 133 | service, err := dbusutil.NewSessionService() 134 | if err != nil { 135 | logger.Warning(err) 136 | os.Exit(1) 137 | } 138 | 139 | xsManager, err := xsettings.Start(xConn, recommendedScaleFactor, service, &display.ScaleFactorsHelper) 140 | if err != nil { 141 | logger.Warning(err) 142 | } else { 143 | globalXSManager = xsManager 144 | } 145 | 146 | err = display.Start(service) 147 | if err != nil { 148 | logger.Warning("start display part1 failed:", err) 149 | } 150 | 151 | // 启动 display 模块的后一部分 152 | go func() { 153 | err := display.StartPart2() 154 | if err != nil { 155 | logger.Warning("start display part2 failed:", err) 156 | } 157 | }() 158 | 159 | err = gsettings.StartMonitor() 160 | if err != nil { 161 | logger.Warning("gsettings start monitor failed:", err) 162 | } 163 | 164 | sysBus, err := dbus.SystemBus() 165 | if err != nil { 166 | logger.Warning(err) 167 | os.Exit(1) 168 | } 169 | sysSignalLoop := dbusutil.NewSignalLoop(sysBus, 10) 170 | sysSignalLoop.Start() 171 | 172 | go func() { 173 | logger.Info("systemd-notify --ready") 174 | cmd := exec.Command("systemd-notify", "--ready") 175 | cmd.Run() 176 | }() 177 | 178 | service.Wait() 179 | } 180 | 181 | func doSetLogLevel(level log.Priority) { 182 | logger.SetLogLevel(level) 183 | if !_useWayland { 184 | display.SetLogLevel(level) 185 | } else { 186 | wl_display.SetLogLevel(level) 187 | } 188 | // watchdog.SetLogLevel(level) 189 | } 190 | 191 | func isInVM() (bool, error) { 192 | cmd := exec.Command("systemd-detect-virt", "-v", "-q") 193 | err := cmd.Start() 194 | if err != nil { 195 | return false, err 196 | } 197 | 198 | err = cmd.Wait() 199 | return err == nil, nil 200 | } 201 | -------------------------------------------------------------------------------- /xsettings/xsettings_writer.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package xsettings 6 | 7 | import ( 8 | "bytes" 9 | "encoding/binary" 10 | "io" 11 | ) 12 | 13 | func (info *xsDataInfo) modifyProperty(setting xsSetting) xsItemInfos { 14 | var ( 15 | tmp xsItemInfos 16 | items = info.items 17 | ) 18 | 19 | for _, item := range items { 20 | if item.header.name == setting.prop { 21 | var ptr = &item 22 | item.header.lastChangeSerial++ 23 | ptr.changePropValue(setting.value) 24 | } 25 | tmp = append(tmp, item) 26 | } 27 | 28 | return tmp 29 | } 30 | 31 | func (item *xsItemInfo) changePropValue(value interface{}) { 32 | switch item.header.sType { 33 | case settingTypeInteger: 34 | item.changeValueInteger(value.(int32)) 35 | case settingTypeString: 36 | item.changeValueString(value.(string)) 37 | case settingTypeColor: 38 | item.changeValueColor(value.([4]uint16)) 39 | } 40 | } 41 | 42 | func (item *xsItemInfo) changeValueInteger(value int32) { 43 | v, ok := item.value.(*integerValueInfo) 44 | if !ok || v.value == value { 45 | return 46 | } 47 | 48 | v.value = value 49 | } 50 | 51 | func (item *xsItemInfo) changeValueString(value string) { 52 | v, ok := item.value.(*stringValueInfo) 53 | if !ok || v.value == value { 54 | return 55 | } 56 | 57 | v.length = uint32(len(value)) 58 | v.value = value 59 | } 60 | 61 | func (item *xsItemInfo) changeValueColor(value [4]uint16) { 62 | v, ok := item.value.(*colorValueInfo) 63 | if !ok || (v.red == value[0] && v.green == value[1] && 64 | v.blue == value[2] && v.alpha == value[3]) { 65 | return 66 | } 67 | 68 | v.red = value[0] 69 | v.green = value[1] 70 | v.blue = value[2] 71 | v.alpha = value[3] 72 | } 73 | 74 | func marshalSettingData(info *xsDataInfo) []byte { 75 | var buf = new(bytes.Buffer) 76 | 77 | writeInteger(buf, &info.byteOrder) 78 | writeSkip(buf, 3) 79 | writeInteger(buf, &info.serial) 80 | writeInteger(buf, &info.numSettings) 81 | for _, item := range info.items { 82 | writeXSItemInfo(buf, &item) 83 | } 84 | 85 | return buf.Bytes() 86 | } 87 | 88 | func newXSItemInteger(prop string, v int32) *xsItemInfo { 89 | var item = xsItemInfo{ 90 | header: newXSItemHeader(prop), 91 | value: &integerValueInfo{ 92 | value: v, 93 | }, 94 | } 95 | 96 | item.header.sType = settingTypeInteger 97 | return &item 98 | } 99 | 100 | func newXSItemString(prop string, v string) *xsItemInfo { 101 | var item = xsItemInfo{ 102 | header: newXSItemHeader(prop), 103 | } 104 | item.header.sType = settingTypeString 105 | 106 | var value = stringValueInfo{ 107 | length: uint32(len(v)), 108 | value: v, 109 | } 110 | 111 | item.value = &value 112 | return &item 113 | } 114 | 115 | func newXSItemColor(prop string, v [4]uint16) *xsItemInfo { 116 | var item = xsItemInfo{ 117 | header: newXSItemHeader(prop), 118 | } 119 | item.header.sType = settingTypeColor 120 | 121 | var value = colorValueInfo{ 122 | red: v[0], 123 | green: v[1], 124 | blue: v[2], 125 | alpha: v[3], 126 | } 127 | 128 | item.value = &value 129 | return &item 130 | } 131 | 132 | func newXSItemHeader(prop string) *xsItemHeader { 133 | var header = xsItemHeader{ 134 | nameLen: uint16(len(prop)), 135 | name: prop, 136 | lastChangeSerial: 1, 137 | } 138 | return &header 139 | } 140 | 141 | func writeSkip(writer io.Writer, num int) { 142 | var buf = make([]byte, num) 143 | err := binary.Write(writer, defaultByteOrder, buf) 144 | if err != nil { 145 | logger.Warning(err) 146 | } 147 | } 148 | 149 | func writeInteger(writer io.Writer, v interface{}) { 150 | err := binary.Write(writer, defaultByteOrder, v) 151 | if err != nil { 152 | logger.Warning(err) 153 | } 154 | } 155 | 156 | func writeString(writer io.Writer, v string) { 157 | err := binary.Write(writer, defaultByteOrder, []byte(v)) 158 | if err != nil { 159 | logger.Warning(err) 160 | } 161 | } 162 | 163 | func writeXSItemInfo(writer io.Writer, item *xsItemInfo) { 164 | writeXSInfoHeader(writer, item.header) 165 | 166 | switch item.header.sType { 167 | case settingTypeInteger: 168 | writeXSValueInteger(writer, item.value.(*integerValueInfo)) 169 | case settingTypeString: 170 | writeXSValueString(writer, item.value.(*stringValueInfo)) 171 | case settingTypeColor: 172 | writeXSValueColor(writer, item.value.(*colorValueInfo)) 173 | } 174 | } 175 | 176 | func writeXSInfoHeader(writer io.Writer, header *xsItemHeader) { 177 | writeInteger(writer, &header.sType) 178 | writeSkip(writer, 1) 179 | writeInteger(writer, &header.nameLen) 180 | writeString(writer, header.name) 181 | writeSkip(writer, pad(int(header.nameLen))) 182 | writeInteger(writer, &header.lastChangeSerial) 183 | } 184 | 185 | func writeXSValueInteger(writer io.Writer, v *integerValueInfo) { 186 | writeInteger(writer, &v.value) 187 | } 188 | 189 | func writeXSValueString(writer io.Writer, v *stringValueInfo) { 190 | writeInteger(writer, &v.length) 191 | writeString(writer, v.value) 192 | writeSkip(writer, pad(int(v.length))) 193 | } 194 | 195 | func writeXSValueColor(writer io.Writer, v *colorValueInfo) { 196 | writeInteger(writer, &v.red) 197 | writeInteger(writer, &v.green) 198 | writeInteger(writer, &v.blue) 199 | writeInteger(writer, &v.alpha) 200 | } 201 | -------------------------------------------------------------------------------- /xsettings/xsettings_ifc.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package xsettings 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | 11 | dbus "github.com/godbus/dbus/v5" 12 | "github.com/linuxdeepin/go-lib/dbusutil" 13 | ) 14 | 15 | var ( 16 | errPropNotFound = fmt.Errorf("this property not found") 17 | errPropTypeNotMatch = fmt.Errorf("this property's type not match") 18 | ) 19 | 20 | func (m *XSManager) ListProps() (string, *dbus.Error) { 21 | datas, err := getSettingPropValue(m.owner, m.conn) 22 | if err != nil { 23 | return "", dbusutil.ToError(err) 24 | } 25 | 26 | infos := unmarshalSettingData(datas) 27 | if infos == nil || len(infos.items) == 0 { 28 | return "", nil 29 | } 30 | return infos.items.listProps(), nil 31 | } 32 | 33 | func (m *XSManager) SetInteger(prop string, v int32) *dbus.Error { 34 | var setting = xsSetting{ 35 | sType: settingTypeInteger, 36 | prop: prop, 37 | value: v, 38 | } 39 | 40 | err := m.setSettings([]xsSetting{setting}) 41 | if err != nil { 42 | logger.Debugf("Set '%s' to '%v' failed: %v", prop, v, err) 43 | return dbusutil.ToError(err) 44 | } 45 | err = m.setGSettingsByXProp(prop, v) 46 | if err != nil { 47 | return dbusutil.ToError(err) 48 | } 49 | 50 | return nil 51 | } 52 | 53 | func (m *XSManager) GetInteger(prop string) (int32, *dbus.Error) { 54 | v, sType, err := m.getSettingValue(prop) 55 | if err != nil { 56 | logger.Debugf("Get '%s' value failed: %v", prop, err) 57 | return 0, dbusutil.ToError(err) 58 | } 59 | 60 | if sType != settingTypeInteger { 61 | return 0, dbusutil.ToError(errPropTypeNotMatch) 62 | } 63 | 64 | return v.(*integerValueInfo).value, nil 65 | } 66 | 67 | func (m *XSManager) SetString(prop, v string) *dbus.Error { 68 | err := m.SetStringInternal(prop, v) 69 | return dbusutil.ToError(err) 70 | } 71 | 72 | func (m *XSManager) SetStringInternal(prop, v string) error { 73 | var setting = xsSetting{ 74 | sType: settingTypeString, 75 | prop: prop, 76 | value: v, 77 | } 78 | 79 | err := m.setSettings([]xsSetting{setting}) 80 | if err != nil { 81 | logger.Debugf("Set '%s' to '%v' failed: %v", prop, v, err) 82 | return err 83 | } 84 | return m.setGSettingsByXProp(prop, v) 85 | } 86 | 87 | func (m *XSManager) GetString(prop string) (string, *dbus.Error) { 88 | str, err := m.GetStringInternal(prop) 89 | return str, dbusutil.ToError(err) 90 | } 91 | 92 | func (m *XSManager) GetStringInternal(prop string) (string, error) { 93 | v, sType, err := m.getSettingValue(prop) 94 | if err != nil { 95 | logger.Debugf("Get '%s' value failed: %v", prop, err) 96 | return "", err 97 | } 98 | 99 | if sType != settingTypeString { 100 | return "", errPropTypeNotMatch 101 | } 102 | 103 | return v.(*stringValueInfo).value, nil 104 | } 105 | 106 | func (m *XSManager) SetColor(prop string, v []uint16) *dbus.Error { 107 | if len(v) != 4 { 108 | return dbusutil.ToError(errors.New("length of value is not 4")) 109 | } 110 | 111 | var val [4]uint16 112 | copy(val[:], v) 113 | 114 | var setting = xsSetting{ 115 | sType: settingTypeColor, 116 | prop: prop, 117 | value: val, 118 | } 119 | 120 | err := m.setSettings([]xsSetting{setting}) 121 | if err != nil { 122 | logger.Debugf("Set '%s' to '%v' failed: %v", prop, val, err) 123 | return dbusutil.ToError(err) 124 | } 125 | err = m.setGSettingsByXProp(prop, val) 126 | return dbusutil.ToError(err) 127 | } 128 | 129 | func (m *XSManager) GetColor(prop string) ([]uint16, *dbus.Error) { 130 | v, sType, err := m.getSettingValue(prop) 131 | if err != nil { 132 | logger.Debugf("Get '%s' value failed: %v", prop, err) 133 | return nil, dbusutil.ToError(err) 134 | } 135 | 136 | if sType != settingTypeColor { 137 | return nil, dbusutil.ToError(errPropTypeNotMatch) 138 | } 139 | 140 | tmp := v.(*colorValueInfo) 141 | 142 | return []uint16{tmp.red, tmp.green, tmp.blue, tmp.alpha}, nil 143 | } 144 | 145 | func (m *XSManager) getSettingValue(prop string) (interface{}, uint8, error) { 146 | m.settingsLocker.RLock() 147 | defer m.settingsLocker.RUnlock() 148 | datas, err := getSettingPropValue(m.owner, m.conn) 149 | if err != nil { 150 | return nil, 0, err 151 | } 152 | 153 | xsInfo := unmarshalSettingData(datas) 154 | item := xsInfo.getPropItem(prop) 155 | if item == nil { 156 | return nil, 0, errPropNotFound 157 | } 158 | 159 | return item.value, item.header.sType, nil 160 | } 161 | 162 | func (m *XSManager) setGSettingsByXProp(prop string, v interface{}) error { 163 | info := gsInfos.getByXSKey(prop) 164 | if info == nil { 165 | return errPropNotFound 166 | } 167 | 168 | return info.setValue(m.cfgHelper, v) 169 | } 170 | 171 | func (m *XSManager) GetScaleFactor() (float64, *dbus.Error) { 172 | return m.getScaleFactor(), nil 173 | } 174 | 175 | func (m *XSManager) SetScaleFactor(scale float64) *dbus.Error { 176 | err := m.setScreenScaleFactors(singleToMapSF(scale), true) 177 | return dbusutil.ToError(err) 178 | } 179 | 180 | func (m *XSManager) SetScreenScaleFactors(factors map[string]float64) *dbus.Error { 181 | err := m.setScreenScaleFactors(factors, true) 182 | return dbusutil.ToError(err) 183 | } 184 | 185 | func (m *XSManager) GetScreenScaleFactors() (map[string]float64, *dbus.Error) { 186 | v := m.getScreenScaleFactors() 187 | return v, nil 188 | } 189 | -------------------------------------------------------------------------------- /display/config_v3_3.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "encoding/json" 9 | "os" 10 | "sort" 11 | "strings" 12 | ) 13 | 14 | type ScreenConfigV3D3 struct { 15 | Name string 16 | Primary string 17 | BaseInfos []*MonitorConfigV3D3 18 | } 19 | 20 | func (sc *ScreenConfigV3D3) toMonitorConfigs(m *Manager) []*MonitorConfigV5 { 21 | result := make([]*MonitorConfigV5, len(sc.BaseInfos)) 22 | var brightness float64 23 | for idx, bi := range sc.BaseInfos { 24 | for brightnessName, value := range m.Brightness { 25 | if brightnessName == bi.Name { 26 | brightness = value 27 | } 28 | } 29 | primary := bi.Name == sc.Primary 30 | result[idx] = &MonitorConfigV5{ 31 | UUID: bi.UUID, 32 | Name: bi.Name, 33 | Enabled: bi.Enabled, 34 | X: bi.X, 35 | Y: bi.Y, 36 | Width: bi.Width, 37 | Height: bi.Height, 38 | Rotation: bi.Rotation, 39 | Reflect: bi.Reflect, 40 | RefreshRate: bi.RefreshRate, 41 | Brightness: brightness, 42 | Primary: primary, 43 | } 44 | } 45 | return result 46 | } 47 | 48 | func (sc *ScreenConfigV3D3) toOtherConfigs(m *Manager) []*MonitorConfigV5 { 49 | result := make([]*MonitorConfigV5, len(sc.BaseInfos)) 50 | var brightness float64 51 | for idx, bi := range sc.BaseInfos { 52 | for brightnessName, value := range m.Brightness { 53 | if brightnessName == bi.Name { 54 | brightness = value 55 | } 56 | } 57 | primary := bi.Name == sc.Primary 58 | result[idx] = &MonitorConfigV5{ 59 | UUID: bi.UUID, 60 | Name: bi.Name, 61 | Enabled: bi.Enabled, 62 | X: bi.X, 63 | Y: bi.Y, 64 | Width: bi.Width, 65 | Height: bi.Height, 66 | Rotation: bi.Rotation, 67 | Reflect: bi.Reflect, 68 | RefreshRate: bi.RefreshRate, 69 | Brightness: brightness, 70 | Primary: primary, 71 | } 72 | } 73 | return result 74 | } 75 | 76 | type MonitorConfigV3D3 struct { 77 | UUID string // sum md5 of name and modes, for config 78 | Name string 79 | Enabled bool 80 | X int16 81 | Y int16 82 | Width uint16 83 | Height uint16 84 | Rotation uint16 85 | Reflect uint16 86 | RefreshRate float64 87 | } 88 | 89 | type ConfigV3D3 map[string]*ScreenConfigV3D3 90 | 91 | func loadConfigV3D3(filename string) (ConfigV3D3, error) { 92 | // #nosec G304 93 | data, err := os.ReadFile(filename) 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | var c ConfigV3D3 99 | err = json.Unmarshal(data, &c) 100 | if err != nil { 101 | return nil, err 102 | } 103 | 104 | return c, nil 105 | } 106 | 107 | // 需要 Manager 的 Brightness,gsColorTemperatureMode, gsColorTemperatureManual 108 | // 还会影响 Manager 的 DisplayMode 109 | func (c ConfigV3D3) toConfig(m *Manager) ConfigV5 { 110 | newConfig := make(ConfigV5) 111 | var brightness float64 112 | for id, sc := range c { 113 | cfgKey := parseConfigKey(id) 114 | jId := cfgKey.getJoinedId() 115 | if cfgKey.name == "" { 116 | // 单屏幕,可设置分辨率 117 | if len(cfgKey.idFields) == 1 && 118 | len(sc.BaseInfos) == 1 { 119 | 120 | bi := sc.BaseInfos[0] 121 | if bi != nil { 122 | for brightnessName, value := range m.Brightness { 123 | if brightnessName == bi.Name { 124 | brightness = value 125 | } 126 | } 127 | 128 | newConfig[jId] = &ScreenConfigV5{ 129 | Mirror: nil, 130 | Extend: nil, 131 | OnlyOne: nil, 132 | Single: &SingleModeConfigV5{ 133 | Monitor: &MonitorConfigV5{ 134 | UUID: bi.UUID, 135 | Name: bi.Name, 136 | Enabled: bi.Enabled, 137 | X: bi.X, 138 | Y: bi.Y, 139 | Width: bi.Width, 140 | Height: bi.Height, 141 | Rotation: bi.Rotation, 142 | Reflect: bi.Reflect, 143 | RefreshRate: bi.RefreshRate, 144 | Brightness: brightness, 145 | Primary: true, 146 | }, 147 | ColorTemperatureMode: m.gsColorTemperatureMode, 148 | ColorTemperatureManual: m.gsColorTemperatureManual, 149 | }, 150 | } 151 | } 152 | } 153 | } else { 154 | screenCfg := newConfig[jId] 155 | if screenCfg == nil { 156 | screenCfg = &ScreenConfigV5{} 157 | newConfig[jId] = screenCfg 158 | } 159 | 160 | configs := sc.toMonitorConfigs(m) 161 | //只有合并和拆分模式 只判断两个屏 162 | if configs[0].X == configs[1].X { 163 | screenCfg.setModeConfigs(DisplayModeMirror, m.gsColorTemperatureMode, m.gsColorTemperatureManual, configs) 164 | //如果升级之前是自定义模式.重新判断是拆分/合并模式 165 | if m.DisplayMode == DisplayModeCustom { 166 | m.setDisplayMode(DisplayModeMirror) 167 | } 168 | } else { 169 | screenCfg.setModeConfigs(DisplayModeExtend, m.gsColorTemperatureMode, m.gsColorTemperatureManual, configs) 170 | //如果升级之前是自定义模式.重新判断是拆分/合并模式 171 | if m.DisplayMode == DisplayModeCustom { 172 | m.setDisplayMode(DisplayModeExtend) 173 | } 174 | 175 | } 176 | } 177 | } 178 | return newConfig 179 | } 180 | 181 | type configKey struct { 182 | name string 183 | idFields []string 184 | } 185 | 186 | func (ck *configKey) getJoinedId() string { 187 | return strings.Join(ck.idFields, monitorsIdDelimiter) 188 | } 189 | 190 | func parseConfigKey(str string) configKey { 191 | var name string 192 | var idFields []string 193 | idx := strings.LastIndex(str, customModeDelim) 194 | if idx == -1 { 195 | idFields = strings.Split(str, monitorsIdDelimiter) 196 | } else { 197 | name = str[:idx] 198 | idFields = strings.Split(str[idx+1:], monitorsIdDelimiter) 199 | } 200 | 201 | sort.Strings(idFields) 202 | return configKey{ 203 | name: name, 204 | idFields: idFields, 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /display/touchscreen.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "sync" 9 | 10 | "github.com/godbus/dbus/v5" 11 | "github.com/linuxdeepin/dde-api/dxinput" 12 | "github.com/linuxdeepin/dde-api/dxinput/common" 13 | dxutils "github.com/linuxdeepin/dde-api/dxinput/utils" 14 | x "github.com/linuxdeepin/go-x11-client" 15 | "github.com/linuxdeepin/go-x11-client/ext/randr" 16 | ) 17 | 18 | const ( 19 | BusTypeUnknown uint8 = iota 20 | BusTypeUSB 21 | ) 22 | 23 | const ( 24 | rotationReflectAll = randr.RotationReflectX | randr.RotationReflectY 25 | ) 26 | 27 | type Touchscreen struct { 28 | Id int32 29 | Name string 30 | DeviceNode string 31 | Serial string 32 | UUID string 33 | outputName string 34 | busType uint8 35 | width float64 36 | height float64 37 | path dbus.ObjectPath 38 | } 39 | 40 | type dxTouchscreens []*Touchscreen 41 | 42 | var ( 43 | devInfos common.DeviceInfos 44 | touchscreenInfos dxTouchscreens 45 | touchscreenInfosMu sync.Mutex 46 | ) 47 | 48 | func getDeviceInfos(force bool) common.DeviceInfos { 49 | if force || len(devInfos) == 0 { 50 | devInfos = dxutils.ListDevice() 51 | } 52 | 53 | return devInfos 54 | } 55 | 56 | func getXTouchscreenInfo(t *Touchscreen) { 57 | for _, v := range getDeviceInfos(false) { 58 | if v.Type != common.DevTypeTouchscreen { 59 | continue 60 | } 61 | 62 | tmp, _ := dxinput.NewTouchscreenFromDevInfo(v) 63 | data, num := dxutils.GetProperty(tmp.Id, "Device Node") 64 | if len(data) == 0 { 65 | logger.Warningf("could not get DeviceNode for %s (%d)", tmp.Name, tmp.Id) 66 | continue 67 | } 68 | 69 | deviceNode := string(data[:num]) 70 | logger.Warningf("deviceNode: %s", deviceNode) 71 | 72 | logger.Warningf("devNode: %s, deviceNode: %s", t.DeviceNode, deviceNode) 73 | if t.DeviceNode != deviceNode { 74 | continue 75 | } 76 | 77 | t.Id = tmp.Id 78 | } 79 | } 80 | 81 | type TransformationMatrix [9]float32 82 | 83 | func (m *TransformationMatrix) set(row int, col int, v float32) { 84 | m[row*3+col] = v 85 | } 86 | 87 | func (m *TransformationMatrix) setUnity() { 88 | m.set(0, 0, 1) 89 | m.set(1, 1, 1) 90 | m.set(2, 2, 1) 91 | } 92 | 93 | func (m *TransformationMatrix) s4(x02 float32, x12 float32, d1 float32, d2 float32, mainDiag bool) { 94 | m.set(0, 2, x02) 95 | m.set(1, 2, x12) 96 | 97 | if mainDiag { 98 | m.set(0, 0, d1) 99 | m.set(1, 1, d2) 100 | } else { 101 | m.set(0, 0, 0) 102 | m.set(1, 1, 0) 103 | m.set(0, 1, d1) 104 | m.set(1, 0, d2) 105 | } 106 | } 107 | 108 | func genTransformationMatrix(offsetX int16, offsetY int16, 109 | screenWidth uint16, screenHeight uint16, 110 | rotation uint16) TransformationMatrix { 111 | 112 | // 必须新的 X 链接才能获取最新的 WidthInPixels 和 HeightInPixels 113 | xConn, err := x.NewConn() 114 | if err != nil { 115 | logger.Warning("failed to connect to x server") 116 | return genTransformationMatrixAux(offsetX, offsetY, screenWidth, screenHeight, screenWidth, screenHeight, rotation) 117 | } 118 | 119 | // total display size 120 | width := xConn.GetDefaultScreen().WidthInPixels 121 | height := xConn.GetDefaultScreen().HeightInPixels 122 | xConn.Close() 123 | 124 | return genTransformationMatrixAux(offsetX, offsetY, screenWidth, screenHeight, width, height, rotation) 125 | } 126 | 127 | func genTransformationMatrixAux(offsetX int16, offsetY int16, 128 | screenWidth uint16, screenHeight uint16, 129 | totalDisplayWidth uint16, totalDisplayHeight uint16, 130 | rotation uint16) TransformationMatrix { 131 | 132 | var matrix TransformationMatrix 133 | matrix.setUnity() 134 | 135 | x := float32(offsetX) / float32(totalDisplayWidth) 136 | y := float32(offsetY) / float32(totalDisplayHeight) 137 | 138 | w := float32(screenWidth) / float32(totalDisplayWidth) 139 | h := float32(screenHeight) / float32(totalDisplayHeight) 140 | 141 | /* 142 | * There are 16 cases: 143 | * Rotation X Reflection 144 | * Rotation: 0 | 90 | 180 | 270 145 | * Reflection: None | X | Y | XY 146 | * 147 | * They are spelled out instead of doing matrix multiplication to avoid 148 | * any floating point errors. 149 | */ 150 | switch int(rotation) { 151 | case randr.RotationRotate0: 152 | fallthrough 153 | case randr.RotationRotate180 | rotationReflectAll: 154 | matrix.s4(x, y, w, h, true) 155 | 156 | case randr.RotationReflectX | randr.RotationRotate0: 157 | fallthrough 158 | case randr.RotationReflectY | randr.RotationRotate180: 159 | matrix.s4(x+w, y, -w, h, true) 160 | 161 | case randr.RotationReflectY | randr.RotationRotate0: 162 | fallthrough 163 | case randr.RotationReflectX | randr.RotationRotate180: 164 | matrix.s4(x, y+h, w, -h, true) 165 | 166 | case randr.RotationRotate90: 167 | fallthrough 168 | case randr.RotationRotate270 | rotationReflectAll: /* left limited - correct in working zone. */ 169 | matrix.s4(x+w, y, -w, h, false) 170 | 171 | case randr.RotationRotate270: 172 | fallthrough 173 | case randr.RotationRotate90 | rotationReflectAll: /* left limited - correct in working zone. */ 174 | matrix.s4(x, y+h, w, -h, false) 175 | 176 | case randr.RotationRotate90 | randr.RotationReflectX: /* left limited - correct in working zone. */ 177 | fallthrough 178 | case randr.RotationRotate270 | randr.RotationReflectY: /* left limited - correct in working zone. */ 179 | matrix.s4(x, y, w, h, false) 180 | 181 | case randr.RotationRotate90 | randr.RotationReflectY: /* right limited - correct in working zone. */ 182 | fallthrough 183 | case randr.RotationRotate270 | randr.RotationReflectX: /* right limited - correct in working zone. */ 184 | matrix.s4(x+w, y+h, -w, -h, false) 185 | 186 | case randr.RotationRotate180: 187 | fallthrough 188 | case rotationReflectAll | randr.RotationRotate0: 189 | matrix.s4(x+w, y+h, -w, -h, true) 190 | } 191 | 192 | return matrix 193 | } 194 | -------------------------------------------------------------------------------- /display/brightness.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "math" 11 | "os" 12 | "path/filepath" 13 | "strings" 14 | 15 | "github.com/linuxdeepin/startdde/display/brightness" 16 | ) 17 | 18 | type InvalidOutputNameError struct { 19 | Name string 20 | } 21 | 22 | func (err InvalidOutputNameError) Error() string { 23 | return fmt.Sprintf("invalid output name %q", err.Name) 24 | } 25 | 26 | func (m *Manager) saveBrightnessInCfg(valueMap map[string]float64) error { 27 | if len(valueMap) == 0 { 28 | return nil 29 | } 30 | changed := false 31 | m.modifySuitableSysMonitorConfigs(func(configs SysMonitorConfigs) SysMonitorConfigs { 32 | for _, config := range configs { 33 | v, ok := valueMap[config.Name] 34 | if ok { 35 | config.Brightness = v 36 | } else { 37 | // 存在当从wayland切换到x11后,在wayland中设置过显示配置,此时配置文件中Name与切换到x11之后中的Name不匹配 38 | // 因此当失败时,在通过uuid查找一次,把Name改写,亮度不变 39 | monitors := m.getConnectedMonitors() 40 | for name := range valueMap { 41 | monitor := monitors.GetByName(name) 42 | if monitor == nil { 43 | logger.Warning("call GetByName failed: ", name) 44 | continue 45 | } 46 | 47 | if config.UUID == monitor.uuid { 48 | config.Name = name 49 | config.Brightness = v 50 | } 51 | } 52 | } 53 | changed = true 54 | } 55 | return configs 56 | }) 57 | 58 | if !changed { 59 | return nil 60 | } 61 | 62 | err := m.saveSysConfig("brightness changed") 63 | return err 64 | } 65 | 66 | func (m *Manager) changeBrightness(raised bool) error { 67 | var step = 0.05 68 | if m.MaxBacklightBrightness < 100 && m.MaxBacklightBrightness != 0 { 69 | step = 1 / float64(m.MaxBacklightBrightness) 70 | } 71 | if !raised { 72 | step = -step 73 | } 74 | 75 | monitors := m.getConnectedMonitors() 76 | 77 | successMap := make(map[string]float64) 78 | for _, monitor := range monitors { 79 | // 如果此显示器不支持亮度调节,则退出 80 | if ok, err := m.CanSetBrightness(monitor.Name); !ok { 81 | logger.Warning("call CanSetBrightness failed: ", err) 82 | continue 83 | } 84 | 85 | v, ok := m.Brightness[monitor.Name] 86 | if !ok { 87 | v = 1.0 88 | } 89 | 90 | var br float64 91 | br = v + step 92 | if br > 1.0 { 93 | br = 1.0 94 | } 95 | if br < 0.0 { 96 | br = 0.0 97 | } 98 | logger.Debug("[changeBrightness] will set to:", monitor.Name, br) 99 | err := m.setBrightnessAndSync(monitor.Name, br) 100 | if err != nil { 101 | logger.Warning(err) 102 | continue 103 | } 104 | successMap[monitor.Name] = br 105 | } 106 | err := m.saveBrightnessInCfg(successMap) 107 | if err != nil { 108 | logger.Warning(err) 109 | } 110 | return nil 111 | } 112 | 113 | func (m *Manager) getSavedBrightnessTable() (map[string]float64, error) { 114 | value := m.settings.GetString(gsKeyBrightness) 115 | if value == "" { 116 | return nil, nil 117 | } 118 | var result map[string]float64 119 | err := json.Unmarshal([]byte(value), &result) 120 | if err != nil { 121 | return nil, err 122 | } 123 | return result, nil 124 | } 125 | 126 | func (m *Manager) initBrightness() { 127 | brightnessTable, err := m.getSavedBrightnessTable() 128 | if err != nil { 129 | logger.Warning(err) 130 | } 131 | m.Brightness = brightnessTable 132 | } 133 | 134 | func (m *Manager) getBrightnessSetter() string { 135 | // NOTE: 特殊处理龙芯笔记本亮度设置问题 136 | blDir := "/sys/class/backlight/loongson" 137 | _, err := os.Stat(blDir) 138 | if err == nil { 139 | _, err := os.Stat(filepath.Join(blDir, "device/edid")) 140 | if err != nil { 141 | return "backlight" 142 | } 143 | } 144 | 145 | return m.settings.GetString(gsKeySetter) 146 | } 147 | 148 | // see also: gnome-desktop/libgnome-desktop/gnome-rr.c 149 | // 150 | // '_gnome_rr_output_name_is_builtin_display' 151 | func (m *Manager) isBuiltinMonitor(name string) bool { 152 | name = strings.ToLower(name) 153 | switch { 154 | case strings.HasPrefix(name, "vga"): 155 | return false 156 | case strings.HasPrefix(name, "hdmi"): 157 | return false 158 | 159 | case strings.HasPrefix(name, "dvi"): 160 | return true 161 | case strings.HasPrefix(name, "lvds"): 162 | // Most drivers use an "LVDS" prefix 163 | return true 164 | case strings.HasPrefix(name, "lcd"): 165 | // fglrx uses "LCD" in some versions 166 | return true 167 | case strings.HasPrefix(name, "edp"): 168 | // eDP is for internal built-in panel connections 169 | return true 170 | case strings.HasPrefix(name, "dsi"): 171 | return true 172 | case name == "default": 173 | return true 174 | } 175 | return false 176 | } 177 | 178 | func (m *Manager) setMonitorBrightness(monitor *Monitor, brightnessValue float64, temperature int) error { 179 | if !isValidColorTempValue(int32(temperature)) { 180 | temperature = defaultTemperatureManual 181 | } 182 | 183 | isBuiltin := m.isBuiltinMonitor(monitor.Name) 184 | err := brightness.Set(brightnessValue, temperature, m.getBrightnessSetter(), isBuiltin, 185 | monitor.ID, m.xConn) 186 | return err 187 | } 188 | 189 | func (m *Manager) setBrightnessAux(fake bool, name string, value float64) error { 190 | monitors := m.getConnectedMonitors() 191 | monitor := monitors.GetByName(name) 192 | if monitor == nil { 193 | return InvalidOutputNameError{Name: name} 194 | } 195 | 196 | monitor.PropsMu.RLock() 197 | enabled := monitor.Enabled 198 | monitor.PropsMu.RUnlock() 199 | 200 | value = math.Round(value*1000) / 1000 // 通过该方法,用来对亮度值(亮度值范围为0-1)四舍五入保留小数点后三位有效数字 201 | if !fake && enabled { 202 | temperature := m.getColorTemperatureValue() 203 | // 保持最小亮度,不能全黑 204 | if value <= 0.1 { 205 | value = 0.1 206 | } 207 | err := m.setMonitorBrightness(monitor, value, temperature) 208 | if err != nil { 209 | logger.Warningf("failed to set brightness for %s: %v", name, err) 210 | return err 211 | } 212 | } 213 | 214 | monitor.setPropBrightnessWithLock(value) 215 | 216 | return nil 217 | } 218 | 219 | func (m *Manager) setBrightness(name string, value float64) error { 220 | return m.setBrightnessAux(false, name, value) 221 | } 222 | 223 | func (m *Manager) setBrightnessAndSync(name string, value float64) error { 224 | err := m.setBrightness(name, value) 225 | if err == nil { 226 | m.syncPropBrightness() 227 | } 228 | return err 229 | } 230 | -------------------------------------------------------------------------------- /wl_display/config.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | package display 6 | 7 | import ( 8 | "encoding/json" 9 | "os" 10 | "path/filepath" 11 | 12 | "github.com/davecgh/go-spew/spew" 13 | "github.com/linuxdeepin/go-lib/log" 14 | "github.com/linuxdeepin/go-lib/xdg/basedir" 15 | ) 16 | 17 | const configVersion = "4.0" 18 | 19 | var ( 20 | configFile string 21 | configVersionFile string 22 | ) 23 | 24 | func init() { 25 | cfgDir := filepath.Join(basedir.GetUserConfigDir(), "deepin/startdde") 26 | configFile = filepath.Join(cfgDir, "display.json") 27 | configVersionFile = filepath.Join(cfgDir, "config.version") 28 | } 29 | 30 | type Config map[string]*ScreenConfig 31 | 32 | type ScreenConfig struct { 33 | Custom []*CustomModeConfig `json:",omitempty"` 34 | Mirror *MirrorModeConfig `json:",omitempty"` 35 | Extend *ExtendModeConfig `json:",omitempty"` 36 | OnlyOne *OnlyOneModeConfig `json:",omitempty"` 37 | Single *MonitorConfig `json:",omitempty"` 38 | } 39 | 40 | type CustomModeConfig struct { 41 | Name string 42 | Monitors []*MonitorConfig 43 | } 44 | 45 | type MirrorModeConfig struct { 46 | Monitors []*MonitorConfig 47 | } 48 | 49 | type ExtendModeConfig struct { 50 | Monitors []*MonitorConfig 51 | } 52 | 53 | type OnlyOneModeConfig struct { 54 | Monitors []*MonitorConfig 55 | } 56 | 57 | func (s *ScreenConfig) getMonitorConfigs(mode uint8, customName string) []*MonitorConfig { 58 | switch mode { 59 | case DisplayModeCustom: 60 | for _, custom := range s.Custom { 61 | if custom.Name == customName { 62 | return custom.Monitors 63 | } 64 | } 65 | case DisplayModeMirror: 66 | if s.Mirror == nil { 67 | return nil 68 | } 69 | return s.Mirror.Monitors 70 | 71 | case DisplayModeExtend: 72 | if s.Extend == nil { 73 | return nil 74 | } 75 | return s.Extend.Monitors 76 | 77 | case DisplayModeOnlyOne: 78 | if s.OnlyOne == nil { 79 | return nil 80 | } 81 | return s.OnlyOne.Monitors 82 | } 83 | 84 | return nil 85 | } 86 | 87 | func getMonitorConfigByUuid(configs []*MonitorConfig, uuid string) *MonitorConfig { 88 | for _, mc := range configs { 89 | if mc.UUID == uuid { 90 | return mc 91 | } 92 | } 93 | return nil 94 | } 95 | 96 | func setMonitorConfigsPrimary(configs []*MonitorConfig, uuid string) { 97 | for _, mc := range configs { 98 | if mc.UUID == uuid { 99 | mc.Primary = true 100 | } else { 101 | mc.Primary = false 102 | } 103 | } 104 | } 105 | 106 | func updateMonitorConfigsName(configs []*MonitorConfig, monitorMap map[uint32]*Monitor) { 107 | for _, mc := range configs { 108 | for _, m := range monitorMap { 109 | if mc.UUID == m.uuid { 110 | mc.Name = m.Name 111 | break 112 | } 113 | } 114 | } 115 | } 116 | 117 | func (s *ScreenConfig) setMonitorConfigs(mode uint8, customName string, configs []*MonitorConfig) { 118 | switch mode { 119 | case DisplayModeCustom: 120 | foundName := false 121 | for _, custom := range s.Custom { 122 | if custom.Name == customName { 123 | foundName = true 124 | custom.Monitors = configs 125 | } 126 | } 127 | 128 | // new custom 129 | if !foundName { 130 | s.Custom = append(s.Custom, &CustomModeConfig{ 131 | Name: customName, 132 | Monitors: configs, 133 | }) 134 | } 135 | 136 | case DisplayModeMirror: 137 | if s.Mirror == nil { 138 | s.Mirror = &MirrorModeConfig{} 139 | } 140 | s.Mirror.Monitors = configs 141 | 142 | case DisplayModeExtend: 143 | if s.Extend == nil { 144 | s.Extend = &ExtendModeConfig{} 145 | } 146 | s.Extend.Monitors = configs 147 | 148 | case DisplayModeOnlyOne: 149 | s.setMonitorConfigsOnlyOne(configs) 150 | } 151 | } 152 | 153 | func (s *ScreenConfig) setMonitorConfigsOnlyOne(configs []*MonitorConfig) { 154 | if s.OnlyOne == nil { 155 | s.OnlyOne = &OnlyOneModeConfig{} 156 | } 157 | oldConfigs := s.OnlyOne.Monitors 158 | var newConfigs []*MonitorConfig 159 | for _, cfg := range configs { 160 | if !cfg.Enabled { 161 | oldCfg := getMonitorConfigByUuid(oldConfigs, cfg.UUID) 162 | if oldCfg != nil { 163 | // 不设置 X,Y 是因为它们总是 0 164 | cfg.Width = oldCfg.Width 165 | cfg.Height = oldCfg.Height 166 | cfg.RefreshRate = oldCfg.RefreshRate 167 | cfg.Rotation = oldCfg.Rotation 168 | cfg.Reflect = oldCfg.Reflect 169 | } else { 170 | continue 171 | } 172 | } 173 | newConfigs = append(newConfigs, cfg) 174 | } 175 | s.OnlyOne.Monitors = newConfigs 176 | } 177 | 178 | type MonitorConfig struct { 179 | UUID string 180 | Name string 181 | Enabled bool 182 | X int16 183 | Y int16 184 | Width uint16 185 | Height uint16 186 | Rotation uint16 187 | Reflect uint16 188 | RefreshRate float64 189 | Primary bool 190 | } 191 | 192 | func loadConfigV4(filename string) (Config, error) { 193 | data, err := os.ReadFile(filename) 194 | if err != nil { 195 | return nil, err 196 | } 197 | 198 | var c Config 199 | err = json.Unmarshal(data, &c) 200 | if err != nil { 201 | return nil, err 202 | } 203 | 204 | return c, nil 205 | } 206 | 207 | func loadConfig() (config Config) { 208 | cfgVer, err := getConfigVersion(configVersionFile) 209 | if err == nil { 210 | if cfgVer == "3.3" { 211 | cfg0, err := loadConfigV3_3(configFile) 212 | if err == nil { 213 | config = cfg0.toConfig() 214 | } else if !os.IsNotExist(err) { 215 | logger.Warning(err) 216 | } 217 | } 218 | 219 | } else if !os.IsNotExist(err) { 220 | logger.Warning(err) 221 | } 222 | 223 | if len(config) == 0 { 224 | config, err = loadConfigV4(configFile) 225 | if err != nil { 226 | config = make(Config) 227 | if !os.IsNotExist(err) { 228 | logger.Warning(err) 229 | } 230 | } 231 | } 232 | 233 | if logger.GetLogLevel() == log.LevelDebug { 234 | logger.Debug("load config:", spew.Sdump(config)) 235 | } 236 | return 237 | } 238 | 239 | func (c Config) save(filename string) error { 240 | var data []byte 241 | var err error 242 | if logger.GetLogLevel() == log.LevelDebug { 243 | data, err = json.MarshalIndent(c, "", " ") 244 | if err != nil { 245 | return err 246 | } 247 | } else { 248 | data, err = json.Marshal(c) 249 | if err != nil { 250 | return err 251 | } 252 | } 253 | 254 | err = os.WriteFile(filename, data, 0644) 255 | if err != nil { 256 | return err 257 | } 258 | return nil 259 | } 260 | --------------------------------------------------------------------------------