• 首页
  • 加入
  • RSS
  • Tuesday, September 3, 2019

    还记得刚推出屏幕保护功能那会儿,我偶逛论坛,围观大家对这个功能的评价。其中让我印象最深的一句话就是:“一股Windows98风”,总之,评价总结出来就是一个字:“吃藕”。

    大家追求美好事物的诚挚之心深深地打动了我,而且,我个人做事情的风格是喜欢未雨绸缪,在屏幕保护程序开发之初,就已经定好了易于扩展的架构,所以我当时就下定了决心,为大家开发一个非Windows98风格的屏保。

    为了达到绝对“非Windows98”的目的,我特意选择了Windows10中的默认屏保作为参考,在无数个周末的战斗下,最终成功将名为“泡泡”的屏保应用发布到了商店(项目地址:https://github.com/zccrs/screensaver-pp )。

    本着“授人以鱼不如授人以渔”的理念,特地整理了这篇文章协助大家开发一款属于自己的时尚屏保应用。

    正文

    在Linux+X11生态环境中,xscreensaver是最“流行”的屏幕保护程序,有着非常多的屏保资源,所以deepin-screensaver必然要兼容它的资源。

    但是,xscreensaver对屏保资源的扩展方式并不符合deepin的开发理念,因此,deepin-screensaver实现了一套全新的屏保扩展方式。

    支持使用Qt qml模块编写屏保应用,一个标准的屏保应用只需要包含一个 “xx.rcc” 文件,将文件安装到 /usr/lib/deepin-screensaver/resources目录。

    rcc 格式是一个编译之后的Qt资源文件,在这个资源文件中至少要包含两个文件:qml代码文件、屏保封面图。

    image

    图中文件名括号内为其别名,也就是屏保主应用加载文件时能读取到的文件名。

    • qml代码文件:屏保应用的代码入口,会被屏保主程序加载显示

    • 屏保封面图:设置屏保入口显示的预览图,支持svg png jpeg bmp等格式 所有的文件必须以特定的目录结构组织到一个Qt资源文件(qrc文件),以“泡泡”屏保为例:qml.qrc 为其资源文件,包含三个前缀路径

    • /deepin-screensaver/modules:放置屏保应用的主qml文件,此路径下的所有qml文件都会被当做一个独立的屏保应用,因此,项目中的其它文件需要额外建立新的前缀放置

    • /deepin-screensaver/modules/cover:放置屏保应用封面图文件,文件名称必须和modules目录中的qml文件一致,且包含它的 “.qml” 后缀。如图上,qml文件全名为:“pp.qml”,封面图全名为:“pp.qml.svg”。

    • /deepin-screensaver/modules/pp:此前缀不是必须的,用于放置项目中的其它文件。为了不与其它项目产生冲突,建议使用项目名作为目录名称 资源文件最好以项目名称命名,避免和其它屏保应用冲突。另外,大家可能已经发现了,这三个前缀都有一个共同点,那就是以 “/deepin-screensaver/modules"开头,的确,这是一个格式要求,不能随意更改路径。

    主qml文件作为屏保应用的入口,它的根元素一定要设置

    anchors.fill: parent
    

    这样才能确保屏保应用充满整个屏幕。在多屏的情况下下,会创建多个窗口示例,可根据屏幕绘制不同的屏保内容。 项目编译其实很简单,只需要使用Qt提供的rcc命令将qrc文件编译为rcc文件即可,使用qmake构建系统时,可以在pro文件中调用以下命令:

    system(rcc --binary $$_PRO_FILE_PWD_/xx.qrc -o $$_PRO_FILE_PWD_/xx.rcc)
    

    当然,最后不要忘记将 xx.rcc 文件安装到deepin-screensaver所要求的目录。做完这所有的步骤后,回到桌面,在右键菜单中选择“壁纸与屏保”,切换到屏保设置后即可看到新添加的屏保应用。 另外,deepin-screensaver为qml提供了获取当前屏幕截图的接口,只需要为Image项指定特定的路径即可:

    Image {
        anchors.fill: parent
        source: "image://deepin-screensaver/screen/" + Screen.name
    }
    

    由于要获取屏幕名称,上述代码需要 “import QtQuick.Window 2.2” 使用

    后记

    屏保封面图最佳比例为:8:5,推荐使用svg格式,以更好的适应高分屏缩放。

    推荐大家使用Qt Creator作为项目的开发工具,可以方便的编辑 qrc 文件。

    泡泡屏保是一个完整的demo,有任何疑问的地方都可以以其作为参考

    参考

    Thursday, February 21, 2019

    为了节省读者的时间,我先简述一下阅读这篇文章需要了解的知识。

    这篇文章将基于Docker来构建nginx、php和mysql来搭建LNMP环境,和其他教程有所不同的是,需要有一定的Docker基础。

    Docker是一个不错的工具,使我们不需要虚拟机那样的庞然大物就可以轻松的隔离运行的程序,这要感谢Linux的资源分离机制,避免启动一个虚拟机造成了大量资源浪费。

    首先需要在Deepin上安装Docker,添加Docker的deb仓库,并安装docker-ce。

    创建文件

    sudo nano /etc/apt/sources.list.d/docker.list

    写入

    deb [arch=amd64] https://download.docker.com/linux/debian jessie edge

    刷新一下仓库就可以安装了。

    sudo apt update && sudo apt install docker-ce docker-compose

    安装完成后重启一下系统,准备工作就算完成了一半了。

    在家目录创建一个Projects目录,当做我们LNMP的工作目录,创建一个名叫docker-compose.yaml的文件,这是docker-compose的配置文件,我们通过docker-compose这个工具来管理我们的Docker容器。

    所有的镜像均采用最新版本,nginx(1.15.8),php(7.3.2),mysql(8.0.15),如有需要,自行选择不同版本的镜像。

    注意PHP7已经不支持mysql扩展,使用内置的MySQLnd。

    写入以下配置文件:

    version: '3'

    services:
    nginx:
    # 设置容器名字
    container_name: "nginx"
    # 采用最新的nginx
    image: nginx:latest
    # 绑定80端口
    ports:
    - "80:80"
    # 添加php容器的依赖
    depends_on:
    - "php"
    # 绑定数据目录
    volumes:
    - "./volumes/nginx/conf.d:/etc/nginx/conf.d"
    - "./volumes/html:/usr/share/nginx/html"
    restart: always

    php:
    # 设置容器名字
    container_name: "php"
    # 采用最新的php
    image: php:fpm
    # 绑定端口
    ports:
    - "9000:9000"
    # 绑定数据目录
    volumes:
    - "./volumes/html:/var/www/html"
    restart: always

    mysql:
    # 设置容器名字
    container_name: "mysql"
    # 采用最新的mysql
    image: mysql:latest
    # 绑定端口
    ports:
    - "3306:3306"
    # 设置环境变量
    environment:
    - MYSQL_ROOT_PASSWORD=(自己设置密码)
    # 绑定数据目录
    volumes:
    - "./volumes/mysql:/var/lib/mysql"
    restart: always

    创建nginx的配置文件,编辑 ./volumes/nginx/conf.d/nginx.conf

    server {
    listen 80;
    server_name localhost;
    location / {
    root /usr/share/nginx/html;
    index index.html index.htm index.php;
    }
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    root /usr/share/nginx/html;
    }
    location ~ \.php$ {
    fastcgi_pass php:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name;
    include fastcgi_params;
    }
    }

    创建php测试文件,编辑 ./volumes/html/index.php :

    <?php
    phpinfo();
    ?>

    启动docker,第一次需要拉取一下镜像:

    docker-compose up --build -d

    等全部结束以后,就可以访问localhost看到php的信息了。

    通过Docker的方法来使用LNMP,不污染宿主机环境,不会再因为各种依赖问题而搞坏系统,这恰恰是新手容易犯的错误,使用Docker,方便你我。

    Monday, December 17, 2018

    头段时间入了一个大坑儿,大概被坑了有一个月之久,出来之后同事还不忘嘲讽一番:”这么个事情就搞了一个月,看吧,你果然是老了“。听了这句话,心里真是百般滋味,但转念一想,”我年轻的时候做事好像也不怎么快“,顿时也就释怀了 🙂

    这个坑就是”给 FreeRDP 的 RAIL 模式添加托盘支持“,当然,跟所有的需求一样,这么具有总结性而又直指根源的需求描述,绝对不是它最原始的模样——我刚接到这个坑的时候,它是这样的:FreeRDP 的 RAIL 模式下,应用的托盘在我们 DDE 下不显示。请注意这里说得是不显示,而不是后来发现的压根儿没有支持

    FreeRDP

    说到这,可能有读者还不了解 FreeRDP 和 RAIL,所以先简单介绍一下。

    RDP 其实是一个协议名称,全称 Remote Desktop Protocol(远程桌面协议),是微软公司开发的一套用于远程桌面展示和操作的协议,FreeRDP 就是它在开源世界的实现咯。而 RAIL 的全称是 Remote Application Integrated Locally (远程应用本地集成),其实就是非常类似大家熟悉的虚拟机的”无缝模式“,通过将应用的显示跟本地环境相融合,让用户完全感受不到这个应用其实不在本机运行——就是这么一种技术。

    问题也就出在这,我当时第一反应是这么老的技术实现肯定比较完整了,托盘没有显示出来应该是跟 DDE 的兼容性有点小问题,稍微修一下就完了,三下五除二的事情,所以满口答应了下来……

    经过

    既然答应了,硬着头皮也要顶下去的。何况调 BUG 这种事情——不管是不是我们自己的问题——在深度都是家常便饭。慢慢地,调各种项目的 BUG 竟然成了我的一种乐趣——每次开始接手一个新的项目的时候,我都把自己当成了福尔摩斯或者胡八一,或者也可以是其他全世界最聪明的那类人 ?,在通过代码找寻问题线索的过程中,慢慢成为这个项目世界中的主宰,解开真相……

    额……不好意思,白日梦又发作了一会儿。总之,这次也不例外,而且刚好这次在调问题的过程中有记录几个关键环节,所以打算把中间的过程写成日记性质的记录,看看能不能有更好的阅读效果:

    2018-11-14

    从”沈老板“那收到需求,说 FreeRDP 在我们系统上有问题,应用的托盘显示不出来,QQ之类的程序关闭了窗口以后就没办法显示出来了,无法使用。这丫的又拿刘老大来压我……呵呵,想削他。不过看在他快要当爸爸的份上,还是算了。问了下时间要求,大概需要两周左右有初步的结果。不过我自己最近没有什么时间,先把锅丢给了印象中还比较熟悉网络协议的 @Blumia 同学。

    2018-11-15

    从 @Blumia 那收到反馈,可能 FreeRDP 没有实现托盘图标这部分的功能,我怕他一个人搞不定,简单翻了翻 FreeRDP 的项目 wiki 和 RDP 的一些介绍,给了他,让他先帮忙找一下需要补充实现部分的代码结构。

    2018-11-16

    没时间处理。

    @Blumia 搭了测试环境。

    中间几天两人都没有时间处理 FreeRDP事情。

    2018-11-22

    留了少部分时间,看了 FreeRDP 的代码,大概找到了托盘图标相关处理应该在的位置。

    • RAIL 主要接口的实现都在 xf_rail.c 中。
    • 托盘图标相关的处理在 xf_rail_no``t``ify_icon_* 相关的函数,这些函数在 xf_rail_re``gi``s``t``er_``u``pdate_call``b``a``c``ks 里面被注册到 rdpWind``o``wUpdate 对象上。
    • 部署了一份测试服务器的虚拟机。
    • 在 wf_rail.``c 中发现一个 PrintRailI``c``onInfo 函数,放在 xf_``r``ail_notify_i``c``on_common 中打印了获取到的图标的信息,发现能正常获取一些图标的数据。
    • xf 应该是 x11 freerdp 的缩写,而 wf 应该是 windows freerdp 的缩写。 中间又是几天没有时间处理 FreeRDP 的事情。

    2018-11-27

    有半天的时间看 FreeRDP 的代码,同时跟 FreeRDP 的邮件列表发了邮件询问相关技术问题,主要是为了验证自己的想法,没有指望有回复或者什么比较大用处的信息,只是希望如果自己想法是错的,有人及时纠正一下。

    • 图标显示的问题不打算优先处理,现在的问题变成如何让服务端知道了本地用户点了托盘图标。
    • 搜了一下 event 相关的文件,发现 x``f_event.c ,怀疑 X 相关的事件都是在这里面处理的,这个也不用急着去证明,先看看 client 怎么让 server 感知本地的事件。
    • 没有头绪,只好看了一下 RAIL 的 主要协议,发现 Cli``e``nt Notify Ev``en``t
    • 怀疑托盘图标在 client 端(本地端)的事件是通过 ClientNotifyEvent 发送给 server 端的。
    • ClientNotifyEvent 相关:
      • rail_main.``c 中的 Vi``r``tualChann``e``lE``n``tryEx 应该是 RDP 中 RAIL 相关的 channel 处理的函数。

    2018-11-28

    上午继续看了 FreeRDP 的代码。

    • V``i``rtualCha``n``nelEntryEx 中给 RailClientContext 设置的哪些成员函数,有些函数(Server开头的)都是需要真正的 client 去实现的,Client 开头的函数(包括 ClientNotifyEvent)都是默认有实现,但是这些 Client 开头的函数都是在哪调用的呢? 找到重要线索:
    /**
    * The position of the X window can become out of sync with the RDP window
    * if the X window is moved locally by the window manager.  In this event
    * send an update to the RDP server informing it of the new window position
    * and size.
    */
    void xf_rail_adjust_position(xfContext* xfc, xfAppWindow* appWindow)
    {
    RAIL_WINDOW_MOVE_ORDER windowMove;
    
    if (!appWindow->is_mapped || appWindow->local_move.state != LMS_NOT_ACTIVE)
        return;
    
    /* If current window position disagrees with RDP window position, send update to RDP server */
    if (appWindow->x != appWindow->windowOffsetX ||
        appWindow->y != appWindow->windowOffsetY ||
        appWindow->width != appWindow->windowWidth ||
        appWindow->height != appWindow->windowHeight)
    {
        windowMove.windowId = appWindow->windowId;
        /*
         * Calculate new size/position for the rail window(new values for windowOffsetX/windowOffsetY/windowWidth/windowHeight) on the server
         */
        windowMove.left = appWindow->x;
        windowMove.top = appWindow->y;
        windowMove.right = windowMove.left + appWindow->width;
        windowMove.bottom = windowMove.top + appWindow->height;
        xfc->rail->ClientWindowMove(xfc->rail, &windowMove);
    }
    }
    
    • 其中有主动调用 RailClientContext 的 ClientWindowMove 函数。这个函数又是 xf_``e``v``en``t.``c 中 xf_``e``v``en``t_C``o``nfig``u``r``e``Notify 有调用,再加上这个函数的注释说明,差不多能证明所有的 X事件相关的都是在 xf_event.c 中处理的,跟之前的猜测一致。
    • 那样的话如果想发送事件到 server,应该就是在 xf_event.c 中收到我们自己创建的托盘图标的点击事件后,发送一个 ClientNofityEvent 。 尝试在收到 xf_r``a``il_``no``ti``f``y_``i``con_u``p``d``a``te 的时候主动调用一次 Cli``ent``NotifyEvent 看看会发生什么。
    RAIL_NOTIFY_EVENT_ORDER notifyEvent;
    notifyEvent.windowId = orderInfo->windowId;
    notifyEvent.notifyIconId = orderInfo->notifyIconId;
    notifyEvent.message = NIN_SELECT;
    
    xfContext* xfc = (xfContext*) context;
    xfc->rail->ClientNotifyEvent(xfc->rail, &notifyEvent);
    
    • 选择 message 为 NIN_SELECT 是因为根据 r``ail.h 里面仅有的零星注释,只能推测这个可能是针对托盘的。
    • 试了下,没有任何反应。尝试换成 NIN_KEYSELECT ,更不行。
    • 硬着头皮又翻了一下协议,发现 Notificat``i``o``n``I``c``on Info``r``mation 这段,随便翻了一下,看起来没有有用信息。
    • 偶发奇想搜了一下 select 关键字想看一下这个到底是什么意思,偶然发现 Cli``en``t Notify E``ve``nt PDU 这一节(能跟源码 r``ai``l``.h 里面的一些注释对应上),里面有 WM_LBUTTONDOWN 、 WM_LBUTTONUP 等针对托盘图标的动作定义。
    • 真是对自己做事情毛毛躁躁的行为无语了,要不是心血来潮,差点就错过这么重要的信息。
    • 把 message 改成 WM_LBUTTONDOWN 和 WM_LBUTTONUP ,满怀期待。
    • 测试还是无效果……仍旧不死心,怀疑测试程序(@Blumia 同学搞的一个音乐程序)的稳健程度。
    • 使用 TIM 再试,还是不行,不能唤出主窗口。
    • 感觉走进了死胡同。

    2018-11-29

    继续看 FreeRDP 的问题,主窗口隐藏后不能显示的问题太奇怪了,得找一个简单点的程序,排除复杂影响。

    • 让 @zccrs 写了一个简单的窗口程序,定时隐藏、显示窗口。
    • 发现程序窗口隐藏后无法再显示出来……
    • 赶紧给上游报了一个 issue ,希望上游能修复。但是也不能期望上游很快能修复这个问题,所以自己还是尝试看代码……
    • 上游回复还挺快的,但是对方好像是 FreeRDP 目前的维护者,说自己对 RAIL 这部分协议本身还不是特别熟悉。
    • 不能依赖的上游不是好上游,继续看代码,发现在窗口的显示隐藏主要是通过 WINDOW_STATE_ORDER 中的 s``h``o``wState 控制的,处理的函数是 xf_``r``ail_window_``c``o``m``mon ,里面调用了 xf_ShowWin``d``ow 这个函数,但是这个函数在该显示窗口的时候只是调整了一下窗口的最大化、最小化状态,并没有 Map 这个本地窗口。
    • 加了 XMapW``in``dow(xf``c``->di``s``pl``a``y, appWi``n``dow->ha``n``dl``e``) 这行,满心期待 bugfix。
    • 编译代码测试,发现窗口连关闭都不能关闭了……
    • xfContext 的 appWindow 是本地窗口的一个抽象表示。

    2018-12-03

    觉得这个事情没有什么太大的希望了,不过既然已经知道托盘图标的显示方式和事件的发送,但是没有实际实现,到时候”沈老板“来问,也不好说都是在脑子里,干脆先把之前测通但是没有实现的内容实现一下。

    • 托盘窗口加好了,事件也都加上了。
    • 眼看着都快要完美了,就差那么一点问题没有解决,实在是不甘心,继续死磕那个问题。
    • 尝试了各种手段调试,跟整个程序的命令传递,都没有能解决问题。

    2018-12-04

    调试了一天,一遍又一遍看窗口事件,一点一点排除事件处理函数,终于发现了上游犯的一个低级错误,我很怀疑当时作者有没有测试一下 🙁

    做了修复,提交了 PR,并且顺利合并。

    心情终于舒畅了。

    中间有事请假一天

    2018-12-06

    托盘图标也画上了,不过怎么感觉颜色有点偏。

    调了一下颜色的格式(RGBA -> BGRA),图标显示正常了。

    事情终于告一段路了。

    结束

    折腾了这么长时间,事情终于搞定了,这应该是最近一年里面时间拉的最长的 BUG 了。

    实现算是完了,也能使用。但是还有一些细节没有特别完善,已提交提交到上游 一个新PR ,希望能早日合并造福一方用户。

    感想

    感觉我之前对 wine 有偏见,一直比较拒绝使用(或者大量使用)wine 的东西,但是实际上在修复 FreeRDP 的过程中,我竟然觉得这也是一种不错的解决方案……仔细想想,还是 wine 方便一点,至少不需要依赖一个服务端。

    准备入坑 wine 啦 ~(≧▽≦)/~

    Wednesday, December 12, 2018

    最近deepin要添加休眠功能,但是之前测试的通过swapfile来休眠失败了,所以对正在使用swap分区的用户提供休眠功能。但是昨天我在askubuntu上看到有人发了在ubuntu下通过swapfile休眠的方案,今天试了一下,效果良好,觉得可以考虑给deepin也加上这样的功能。

    原文链接: Hibernate and resume from a swap file

    具体步骤是通过uswsusp这个包来做的,uswsusp是一组用户空间工具,用于Linux系统上的休眠(挂起到磁盘)和挂起(挂起到RAM或待机)。详细内容可以在ArchWiki上参考。点这里

    先创建一个和内存同等大小的swapfile,为了确保休眠成功,不能小于内存的容量。

    sudo fallocate -l 16g /swapfile # 我的机子是16G,具体自己修改
    sudo chmod 600 /swapfile
    sudo mkswap /swapfile
    sudo swapon /swapfile
    echo '/swapfile swap swap defaults 0 0' | sudo tee -a /etc/fstab

    安装用户空间软休眠(Userspace Software Suspend)包

    sudo apt install uswsusp

    创建需要的配置文件,只需要创建文件即可。

    sudo touch /etc/uswsusp.conf
    sudo dpkg-reconfigure -pmedium uswsusp

    这时候终端会提醒是否继续,选择Yes,然后会要求你创建一个密码,设置一个密码继续即可。

    此时就可以测试一下功能了,不过我是跳过这个步骤了(比较喜欢作死)。

    修改systemd的hibernate服务,使用uswsusp来代替systemd的功能。

    sudo systemctl edit systemd-hibernate.service

    写入以下内容:

    [Service]
    ExecStart=
    ExecStart=/usr/sbin/s2disk
    ExecStartPost=/bin/run-parts -a post /lib/systemd/system-sleep

    这时候可以使用systemd的命令来测试的,我表示工作的非常正常。

    systemctl hibernate

    执行以后可以看到屏幕上会打印当前保存的进度,然后设备就关机了,此时再开机,等待一会儿以后就看到了背景是我漂亮老婆的锁屏,解锁以后看到工作区还是执行命令前的,一切ok。

    参考以下内容:

    https://askubuntu.com/questions/6769/hibernate-and-resume-from-a-swap-file

    https://wiki.archlinux.org/index.php/Uswsusp

    Wednesday, June 27, 2018

    CUDA(Compute Unified Device Architecture,统一计算架构)是由NVIDIA所推出的一种集成技术,是该公司对于GPGPU的正式名称。通过这个技术,用户可利用NVIDIA的GeForce 8以后的GPU和较新的Quadro GPU进行计算。亦是首次可以利用GPU作为C-编译器的开发环境。NVIDIA营销的时候,往往将编译器与架构混合推广,造成混乱。实际上,CUDA可以兼容OpenCL或者自家的C-编译器。无论是CUDA C-语言或是OpenCL,指令最终都会被驱动程序转换成PTX代码,交由显示核心计算。

    在论坛上看到有些用户希望在deepin下使用CUDA,但是他们采取的做法往往是手动下载nvidia的二进制文件,直接进行安装。

    但是这样会破坏一部分的glx链接,导致卸载的时候无法彻底恢复,结果就是系统因为卸载nvidia驱动而废掉,所以我推荐使用包管理器的方式安装nvidia驱动和cuda相关的东西,尽量不要手动修改。

    需要安装的很少,只有五个包,不过会依赖很多nvidia的库,总量还是有一些的。

    sudo apt install nvidia-cuda-toolkit nvidia-profiler nvidia-visual-profiler nvidia-cuda-doc nvidia-cuda-dev

    nvcc是cuda的编译器,它目前只支持g++5,所以还需要安装g++5。

    sudo apt install g++-5

    然后,重启一下计算机。

    这里有个小栗子,可以用来测试cuda是否能够成功编译和运行

    将以下代码保存为 main.cu

    #include <stdio.h>

    __global__ void vector_add(const int *a, const int *b, int *c) {
    *c = *a + *b;
    }

    int main(void) {
    const int a = 2, b = 5;
    int c = 0;

    int *dev_a, *dev_b, *dev_c;

    cudaMalloc((void **)&dev_a, sizeof(int));
    cudaMalloc((void **)&dev_b, sizeof(int));
    cudaMalloc((void **)&dev_c, sizeof(int));

    cudaMemcpy(dev_a, &a, sizeof(int), cudaMemcpyHostToDevice);
    cudaMemcpy(dev_b, &b, sizeof(int), cudaMemcpyHostToDevice);

    vector_add<<<1, 1>>>(dev_a, dev_b, dev_c);

    cudaMemcpy(&c, dev_c, sizeof(int), cudaMemcpyDeviceToHost);

    printf("%d + %d = %d, Is that right?\n", a, b, c);

    cudaFree(dev_a);
    cudaFree(dev_b);
    cudaFree(dev_c);

    return 0;
    }

    编译:

    nvcc main.cu

    运行:

    ./a.out

    如果一切顺利,在编译的时候就不会有报错,不过在我的环境下nvcc会有架构被弃用的警告,本着只要不error就算没事的原则,我们无视这条警告即可。

    输出结果:

    2 + 5 = 0, Is that right?

    Sunday, June 24, 2018

    笔记本一直使用的bumblebee来省电,毕竟我也不想笔记本的电只够从一张桌子移动到另一张桌子,但是今天在调待机唤醒后dde-dock崩溃的问题,我需要切换到私有驱动下,因为笔记本使用bumblebee需要使用acpi的参数,否则会见图形就死。

    一切准备就绪以后,我开始调试dde-dock,通过codedump已经知道崩溃在wifi列表为空时访问了first节点,但是当我开始测试修复的代码时,发生了很意外的事情,恢复待机以后键盘和触摸板无法使用了。

    虽然之前我也偶尔会用用私有驱动,但是还没遇到过无法键盘和触摸板无法使用的情况。想到论坛好像也有人报了类似的问题,恢复待机以后无wifi和外置键盘无法使用,刚好可以趁这个机会调试一下。

    /var/log/Xorg.0.log里看到了大量的synaptics错误,然后该模块被卸载,键盘则是没看到什么信息。

    尝试重新modprobe synaptics模块,但是失败了,然后在/etc/modprobe.d/nvidia.conf里看到了几行配置。

    # These aliases are defined in *all* nvidia modules.
    # Duplicating them here sets higher precedence and ensures the selected
    # module gets loaded instead of a random first match if more than one
    # version is installed. See #798207.
    #aliaspci:v000010DEd00000E00sv*sd*bc04sc80i00*nvidia
    #aliaspci:v000010DEd00000AA3sv*sd*bc0Bsc40i00*nvidia
    #aliaspci:v000010DEd*sv*sd*bc03sc02i00*nvidia
    #aliaspci:v000010DEd*sv*sd*bc03sc00i00*nvidia

    似乎是通配出错了,匹配到了键盘和触摸板,然后就无法使用了。刚好deepin 15.6升级了nvidia驱动,所以是现在才会出这个问题。

    Tuesday, May 22, 2018

    从零构建 dde-dock 的插件
    本教程将展示一个简单的 dde-dock 插件的开发过程,插件开发者可跟随此步骤为 dde-dock 创造出更多具有丰富功能的插件。

    在本教程中,将创建一个可以实时显示用户家目录(~/)使用情况的小工具。

    插件的工作原理
    dde-dock 插件本质是一个按 Qt 插件标准所开发的共享库文件(so)。通过 dde-dock 预定的规范与提供的接口,共同完成 dde-dock 的功能扩展。

    准备环境
    插件的开发环境可以是任意的,只要是符合 Qt 插件规范及 dde-dock 插件规范的共享库文件,都可以被当作 dde-dock 插件载入。下面以 Qt + qmake 为例进行说明:

    安装依赖
    以 Deepin 15.5.1 环境为基础,至少先安装如下的包:

    • dde-dock-dev
    • qt5-qmake
    • qtbase5-dev-tools
    • libqt5core5a
    • libqt5widgets5
    • pkg-config

    基本的项目结构
    创建必需的项目目录与文件
    插件名称叫做home_monitor,所以创建以下的目录结构:

    home_monitor
    ├── home_monitor.json
    ├── homemonitorplugin.cpp
    ├── homemonitorplugin.h
    └── home_monitor.pro

    Sunday, March 11, 2018

    dde-session-ui里面包含了很多项目,是一个集合,但是其中的代码缺少合理的维护,以至于已经到了必须重构才能继续开发和维护,在支持AD域登录的时候,如果强制加上功能,代码会变得更加糟糕,所以和石博文一块重构了其中非常重要的UserWidget。

    重构前的设计

    重构前的dde-lock和lightdm-deepin-greeter是非常混乱的,处理逻辑都混杂在一块,虽然能看出有基本的结构,但是整体并未解耦。

    重构后的设计

    • 基于User类的处理
    • UserWidget负责提供对用户的处理,暴露出基本的currentUser和LogindUsers。
    • Lock和Greeter的Manager从UserWidget、SessionWidget中获取用户和用户的会话。
    • Manager只负责控件的位置和用户的验证。
    • 背景修改为Manager提供模糊的壁纸,FullBackground只提供绘制。

    重构以后用了大概原代码的1/3,启动速度也快了,感觉世界充满了美好… 就是重构历程太辛苦…

    本次也发现了很多自身的问题,基础并没有学好,很多地方都可以使用更好的处理方式【就是管不住这手…】

    Friday, January 12, 2018

    在阅读本篇文章之前,你需要掌握基本的Qt/C++开发知识。

    注意:本篇文章基于Deepin平台,其他平台请自行补充依赖关系。

    先安装DTK的依赖关系。

    sudo apt install libdtkwidget2 libdtkcore2

    新建Qt项目,编辑pro文件,添加项目依赖。

    CONFIG += c++14 link_pkgconfig
    PKGCONFIG += dtkwidget

    DTK目前有两个组件,一个是提供库功能的core,一个是提供控件的widget。

    修改main.cpp,删除QApplication的相关内容,改为DApplication。

    注意: 使用DTK的组件,需要使用DTK的宏,根据使用的文件来选择对应的宏。

    DWIDGET_USE_NAMESPACE
    DCORE_USE_NAMESPACE

    DTK使用了deepin自己的qt插件,需要在DApplication前调用。

    DApplication::loadDXcbPlugin();
    DApplication app(argc, argv);

    DApplication中提供了很多方法来设置程序的各种信息,具体请看头文件的定义。

    主窗口由DMainWindow提供,新建类,然后添加DMainWindow的头文件和DTKWIDGET的宏。

    #include <DMainWindow>

    DWIDGET_USE_NAMESPACE

    然后修改继承关系,改为继承DMainWindow。DMainWindow提供了一些我们封装的方法。目前为止,该程序的界面已经符合Deepin程序的风格了,我们封装了一些其他控件,使其样式符合我们的风格,如果要在其他Qt程序中使用,也是同样的步骤,载入Qt插件,添加对应的头文件和DTK的宏。

    Wednesday, August 23, 2017

    这里记录了开发topbar中遇到的坑和一些问题。

    使用Qt提供的qxcb方法注册阴影为dock类型,反而处于DESKTOP和NORMAL之间。其实当初并不是想设置为DOCK类型的,因为这样阴影也会在窗口上方,我希望的是阴影在普通程序下方,在桌面上方。今天曹哥来讲窗管的一些坑,讲到窗管是如何控制窗口的,我的阴影其实是被Qt注册成_NET_WM_STATE_BELOW了。
    这里可以看到一些type的介绍

    在_NET_WM_STATE中一共有这么几个类型:

    _NET_WM_STATE_MODAL, ATOM
    _NET_WM_STATE_STICKY, ATOM
    _NET_WM_STATE_MAXIMIZED_VERT, ATOM
    _NET_WM_STATE_MAXIMIZED_HORZ, ATOM
    _NET_WM_STATE_SHADED, ATOM
    _NET_WM_STATE_SKIP_TASKBAR, ATOM
    _NET_WM_STATE_SKIP_PAGER, ATOM
    _NET_WM_STATE_HIDDEN, ATOM
    _NET_WM_STATE_FULLSCREEN, ATOM
    _NET_WM_STATE_ABOVE, ATOM
    _NET_WM_STATE_BELOW, ATOM
    _NET_WM_STATE_DEMANDS_ATTENTION, ATOM

    如果程序被注册成_NET_WM_STATE_BELOW,则会被放置在DESKTOP之上的一层。不是很清楚Qt是出于什么策略,才把我的阴影注册为这个状态,反而是刚好满足了我的需求。

    这要多谢曹哥了,我才终于明白了为什么会这样,以及以后如何正确的设置type。

    DESKTOP1DESKTOP2