自制快速调节鼠标灵敏度的小玩意——SimpMouse

simpmouse_pic

介绍:

这是我最近弄的小玩意,可以识别连接的鼠标,并分别调节每个鼠标的“减速系数”(Constant Deceleration)。

代码托管在Github上:
https://github.com/ukyoi/simpmouse (点此进入项目页面)
欢迎fork和贡献(虽然我觉得应该没什么人fork)。

功能上,选中要调节的鼠标,然后拖动滑杆或输入数值,就可以改变鼠标灵敏度。由于减速系数取值是1到正无穷(应该是正无穷吧……),所以滑杆那里我取了个倒数,左端对应数值为16。直接输入的话则可以接受任何有效的数值。

另外,虽然有Refresh、Apply和Cancel三个按钮,不过这些都是半年前构想中的功能,现在统统用不了,当然更无什么记忆功能。总之只是个很简陋的半成品。

主要用到的东西:Qt做界面、C++、Qt做基本功能(使用qmake进行构建)。
通过运行 xinput list 和 xinput list-props 获取鼠标信息,并用 xinput set-prop 写入Constant Deceleration数值。

关于这个程序的一些废话:

最初想法源于半年前跟人联网用wine玩星际。KDE的鼠标调节功能比较残,指针加速那里即使调到最低,我的鼠标也还是太灵敏(xfce的鼠标调节就很好,可以每个鼠标独立调整,项目也多)。网上查到用xinput可以对鼠标行为进行精细调节,我就打算写个方便的图形工具来用。结果一下拖到现在,星际自然是早就不打了,程序也变成了练手(自娱自乐)之作。至于程序名……由于这个程序对多数人来说实在是没啥大用,请原谅我没在上面花什么心思。

我本以为如此小程序,不会有什么代码量,然而最终代码量却出乎我的意料。当然回报也是有的,我的最大收获大概在于对如何组织程序的结构有了一些经验。我之前只是看过一些编程语言的教材和简单的算法,而对哪怕一个很小的程序应该如何设计和组织则全无经验。通过边写这个程序边进行重构,我对代码该怎么划分、怎么组织都有了一些粗浅的了解。虽然程序本身很小,但我自认为带来的收获还是可观的吧。

C/C++ 包含顺序引发的错误一例

这个错误是我最近在写一个Qt小程序的时候出现的,觉得有一定价值,打算写出来。由于我水平实在太弱,故若高手们看了之后觉得浪费了自己的生命,还请轻拍。

假如我的程序除了main.cpp之外还有3个.h/.cpp组,这里用A.h、B.h、C.h代替:

[code language=”cpp”]
// main.c
#include "A.h";
// Do something…
return 0;
[/code]

[code language=”cpp”]
// A.h
#ifndef A_H
#define A_H

#include "B.h";
#include "C.h";

#endif
[/code]

[code language=”cpp”]
// B.h
#ifndef B_H
#define B_H

class A_Class;

#endif
[/code]

[code language=”cpp”]
// C.h
#ifndef C_H
#define C_H

#include "A.h";
void a_function(A_Class an_arg);

#endif
[/code]

可以看出,C.h的一个函数中要用到B.h中声明的一个叫做A_Class的类。由于A.h包含了C.h和B.h,而C.h又包含了A.h,因此看上去C.h可以通过A.h包含B.h,从而用上B.h中声明的类。

之后通过qmake等等自动写makefile的工具生成makefile,进行编译。然而在编译的时候,编译器却会给出错误,说A_Class没有被声明。而如果把A.h中包含的顺序调换一下,先包含B.h,再包含C.h,编译就能够通过。

问题主要就出在A.h和C.h的相互包含上。A.h先包含了C.h,根据生成的makefile,编译器会先开始处理C.h,而在C.h被处理完毕之前,B.h是不会被处理的,因此编译器会说找不到B.h中声明过的类。而如果把A.h中包含的顺序调换,在处理C.h之前,B.h已经被处理好了,就不会出现找不到声明的情况。

因此为了避免这类问题,最好把头文件间的关系理清,不要出现这种“互相包含”的情况。如果非要用到彼此的东西,可以使用前向声明(但这种方法实际操作起来是很麻烦的)。另外,既然有了#ifndef的保护,凡是要用到的东西统统在头文件里包含一遍,这样也可以确保所有需要用到的东西在用到之前都是准备好的。

Fcitx皮肤查看器

虽说私很懒,不过不忙的时候还是会捣鼓一些东西的,这Fcitx皮肤查看器就是私最近主要在弄的一个。工程方面基本是CSSlayer仁兄打的底子。
配置读取部分是直接调用Fcitx的API,且貌似Fcitx4.1的皮肤配置有所更改,因此应该至少要求Fcitx版本号高于4.1。
虽说只有最基本的功能,输入框还没有写好,边栏还只是把东西一股脑地显示出来,不过大体的形态已经有了,先发出来做个预告。

Fcitx皮肤浏览器
Fcitx皮肤查看器

有兴趣的且有Qt的人可以从github上抓下来玩玩(虽然我认为应该没多少人有兴趣……):
[email protected]:csslayer/fcitx-skin-viewer.git

用C语言重写的会员卡管理程序,欢迎Linux众测试

终于重写完毕了,不过Bug肯定很多,欢迎诸君测试。由于未在Windows下调试,且有磁盘读写的相关代码,若有热心的Windows用户想通过源代码编译还请自行删除可能产生的垃圾文件。

Linux用户可用的二进制程序:

https://skydrive.live.com/?cid=D40A6A1CE1A272AC&id=D40A6A1CE1A272AC%21507&sc=documents

源代码在此,欢迎给出指导:

http://github.com/ukyoi/cardmanage

我不太会用github,所以有些混乱,还请原谅。

最近的动作

好久没码字了。主要原因仍旧是太懒,觉得码字太耽误时间,但其实在互联网上闲逛也很耗时间,每天似乎没做什么事就流逝掉了。

当然,也不是什么事都没做。最近在把私曾经那个用Python写的代码再用C重写一遍……私承认这个想法有点蛋疼,但是这么做还是有一定原因的。首要原因是私的目标是未来使用Qt图形库,写这个文本的会员卡程序只是练练手。PyQt虽然有文档,但私找到的教程都不是很系统也不很容易理解。原生Qt是使用C++的,有详细的官方文档,所以私打算先涉及C/C++,然后把原生Qt学会,再学PyQt这样的语言绑定。所以私并非放弃Python,而是暂时先搁置一下而已。

然而直到开始写,才直到用C写程序是有多么困难。私会员卡管理程序的第一版只有180行python代码。而现在写了200+行C代码,只实现了不到一半的功能。当然也有其他的原因,第一版的程序流程控制有很大问题,误操作之后要从主菜单重新选过,非常麻烦,而现在私在写的时候会尽量考虑到误操作之后怎么处理的问题。然而总体来说,C语言的头文件、声明以及表示代码段的括号等等都是很占地方的。

而且……啃C语言的教材也是相当困难的事情……大概我在这方面没什么天赋吧……

最近的事

其实私本来早就想写一些什么了,但是由于最近的事情实在是多了些,实在很难挤出些时间码这些字。当然其实这只是私给自己犯懒找些借口而已。

也许是这几天压力确实大了些(说实话私真的想不出别的解释,因为私最近还是很关注自己的健康的),前天嗓子开始不适,昨日发展成疼痛,今天貌似疼痛有所减轻,但病灶转移,上午开始流涕,下午便已经开始发烧了。借同学的体温表量了量,37.4摄氏度,不算高。服用两片维C银翘,现在还不算很难受。

下面开始正题……

//

今日看到KDE 4.7 beta已经出了……很多激动人心的特性啊,当然不满意的地方总是有的,比如文件夹图标。总之现在正在更新中,希望打包不要有什么问题才好。

//

前一阵子看到方校长被砸中了。虽然私从情感上觉得很过瘾,但对方校长,对此事还需要冷静地看待。墙确实是越来越高,开放的互联网离我们似乎也越来越远,然而至少互联网还在中国这篇常被割草机推过的土地上艰难生长着。私不知如果墙是不可实现的,官方会不会真的做出拔线这种蠢行为。无论拔线这一行为有多蠢,按照我朝屁民们的生活哲学,这网断个十年八年绝不是什么问题。从方校长自己的角度说,假如上头有了建墙的决策而方校长却不肯合作会怎样?
当然私并不是在说方校长这样做就是正义的。然而即便贵为哈工大、北邮的校长,也不过是个凡人。凡人必须确保自己生存下去,才可能拯救别人。当然说他害了别人,这也不错。但如果他不害别人,也许会有更厉害的人来害。
而对于事件本身。短事件内墙肯定会因此继续提高(事实也确实如此),然而也许能够引起更多人的重视,从长远来看还是很有利的。

//

冷落编程很久之后,私终于又开始继续C语言的学习之路了。其实以前所谓的“没时间”、“记不住”都是些借口而已。有事件用来上推、浏览网站,自然也有事件仔细地看看书,码码字。
至于那个“理发店会员卡管理程序”,暂时的想法是用C重写,并且把“理发店”三个字去掉(因为实在没什么意义)。现在写了一些框架(例如对这小程序其实毫无必要的makefile、头文件),但在搞懂fopen、fwrite这些函数之前还有些重要内容要学习,所以暂时还不能写关键部分。大概要想实现好主要功能至少要等放假了。
为何要用C写呢?主要原因是不再需要一套Python才能运行,因此无论在Linux上还是Win上都可以一键运行了,当然也不必关心Python不同版本语法不同的问题。当然问题也是很多的,代码要重写,跨平台的时候代码要修改,Win下可能还有汉字编码问题,等等……

//

私曾撰过关于兔子的文章,那兔子其实是同楼基础医学系的同学养的。据说一直就吃百家饭,最近又跑到我们这里来了。

//

别的暂时想不起来了,就写到这里吧。

DFS解迷宫的一些想法

写在前面:私不是程序员,也不是学信科的,也只是最近开始没事的时候翻翻C语言的书。因此阁下很可能会觉得下文所述的想法很原始或是阁下早已用过了。私在此只是记录一下自己的想法。

现阶段私学C语言用的书是宋劲杉的《Linux C编程一站式学习》,里面讲深度优先搜索的时候讲的是个走迷宫问题,用1表示墙,0表示路,从左上走到右下。代码如下:

#include <stdio.h>
#define MAX_ROW 5
#define MAX_COL 5

struct point { int row, col; } stack[512];
int top = 0;

void push(struct point p)
{
    stack[top++] = p;
}

struct point pop(void)
{
    return stack[--top];
}

int is_empty(void)
{
    return top == 0;
}

int maze[MAX_ROW][MAX_COL] = {
    0, 1, 0, 0, 0,
    0, 1, 0, 1, 0,
    0, 0, 0, 0, 0,
    0, 1, 1, 1, 0,
    0, 0, 0, 1, 0,
};

void print_maze(void)
{
    int i, j;
    for (i = 0; i < MAX_ROW; i++) {
        for (j = 0; j < MAX_COL; j++)
            printf("%d ", maze[i][j]);
        putchar('n');
    }
    printf("*********n");
}

struct point predecessor[MAX_ROW][MAX_COL] = {
    {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}},
    {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}},
    {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}},
    {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}},
    {{-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}},
};

void visit(int row, int col, struct point pre)
{
    struct point visit_point = { row, col };
    maze[row][col] = 2;
    predecessor[row][col] = pre;
    push(visit_point);
}

int main(void)
{
    struct point p = { 0, 0 };

    maze[p.row][p.col] = 2;
    push(p);

    while (!is_empty()) {
        p = pop();
        if (p.row == MAX_ROW - 1  /* goal */
            && p.col == MAX_COL - 1)
            break;
        if (p.col+1 < MAX_COL     /* right */
            && maze[p.row][p.col+1] == 0)
            visit(p.row, p.col+1, p);
        if (p.row+1 < MAX_ROW     /* down */
            && maze[p.row+1][p.col] == 0)
            visit(p.row+1, p.col, p);
        if (p.col-1 >= 0          /* left */
            && maze[p.row][p.col-1] == 0)
            visit(p.row, p.col-1, p);
        if (p.row-1 >= 0          /* up */
            && maze[p.row-1][p.col] == 0)
            visit(p.row-1, p.col, p);
        print_maze();
    }
    if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1) {
        printf("(%d, %d)n", p.row, p.col);
        while (predecessor[p.row][p.col].row != -1) {
            p = predecessor[p.row][p.col];
            printf("(%d, %d)n", p.row, p.col);
        }
    } else
        printf("No path!n");

    return 0;
}

大概意思就是每次弹出一个栈,找到周围所有的合法步骤然后把它们栈压进栈区,下一轮时再弹出最后压入的栈,如果走入死路就再次弹栈,找另外一条可能路线,直到找到迷宫的解。如果栈空了就说明无路可走,打出“No path !”。
但是找到解之后就比较麻烦。如何输出路径呢?这段代码使用的方法是建立一个predecessor数组,用来表示每个点的前趋,然后从最后一个点顺藤摸瓜往前找,逐渐打印出来。但这个predecessor数组占用的空间很大,且只能从后向前找,不便于找到其中的某个特定步骤。怎么解决这一问题呢?作者宋劲杉出了一道思考题,还问读者能够想出几种方法,可见方法是不止一种的。

在正确理解这段代码之前,私一直认为栈区中至少应该存有正确路径走过的所有的点,后来才明白这个想法是有问题的。由于每个循环开始时栈顶已经弹出了,此时如果再在这一点继续向深处找,新压入的栈就会把刚才弹出的栈顶覆盖掉,这样找到最后整个栈中在极端情况下(例如整个迷宫都没有岔路)可能一个点也不剩。此外一个循环内是4个方向均会搜索,所以如果有岔路,就会压入多个点,但弹出时只会弹出一个点,所以栈中会残留错误道路的点。

怎么修改呢?其实很容易。原来每个循环会从栈顶弹出一个栈,其实找到路径之后,完全可以把这一栈再压回去,比如这样:

int main(void)
{
    struct point p = { 0, 0 };

    maze[p.row][p.col] = 2;
    push(p);

    while (!is_empty()) {
        p = pop();
        if (p.row == MAX_ROW - 1  /* goal */
            && p.col == MAX_COL - 1)
            break;
        if (p.col+1 < MAX_COL     /* right */
            && maze[p.row][p.col+1] == 0) {
            top++;
            visit(p.row, p.col+1, p);
        } else if (p.row+1 < MAX_ROW     /* down */
            && maze[p.row+1][p.col] == 0) {
            top++;
            visit(p.row+1, p.col, p);
        } else if (p.col-1 >= 0          /* left */
            && maze[p.row][p.col-1] == 0) {
            top++;
            visit(p.row, p.col-1, p);
        } else if (p.row-1 >= 0          /* up */
            && maze[p.row-1][p.col] == 0) {
            top++;
            visit(p.row-1, p.col, p);
        }
        print_maze();
    }

    int i;
    if (!is_empty()) {
        for (i=0; i<top; i++)
            printf("(%d, %d)n", stack[i]);
    } else
        printf("No path.n");

    return 0;
}

如果找到了可用的路,由于有了top++,相当于仅仅是读取到栈顶的数据(而栈没有弹出),这样在搜索路径压栈的时候就不会把原来的栈顶覆盖掉,从而保证了栈中存在所有正确路径上的点。解决残留错误道路的点的问题也很容易,只需让一个循环内如果搜索到了一条可以深入的路径就停止(用else实现),否则再搜索下一条路径。如果找到死路栈会逐个弹出,直到岔路口,所以不用担心找不到正解。到最后如果迷宫存在解的话,栈中所保存的就是正确的路径。由于例子中的“栈”是一个数组(当然您可能会反驳说这个例子中stack[]并不是真正意义上的栈,“栈”在理论上应该只能访问顶端的元素,但那只是个形式问题),于是可以很容易地访问任何一步的点,输出的时候正着打反着打跳着打均可,程序中所有和predecessor有关的内容便可以删去了。

但是事实上这个办法在程序效率上是劣化而不是优化,首先虽然不需要predecessor了,但是栈空间消耗变得更大。原本栈空间的消耗和分岔路的多少有关,现在则是和路径的长度有关而与有多少岔路无关。那么栈空间的最小值应该是多大呢?私还没考虑好,但分配为整个迷宫的大小肯定是足够的。还有个问题是原来的方式由于覆盖了之前走过的路径,只保留岔路上其他方向的点,如果找到死路,弹栈后会直接跳到另外一个可能的岔路继续搜索,而修改后的方法如果找到死路,必须沿原路一步步跳回岔路口。再加上top++所用的时间,程序效率比原来要低,而且岔路越多越深入,效率上的差距越明显。

前几天看到个好玩的推,说“情人节我玩一天连连看,消灭一对是一对”。私寻思既然学了DFS和BFS,不如自己也写个文本连连看玩玩?再说吧。

另:为嘛我Tag里写C,wordpress会给自动补全成C++?

教材里的古怪

明明是教C++的,用的居然的都是C的标准库和C的函数。搞得我在网上找的一些资料看得一头雾水……于是乎仔细研读了一下,书里面教的其实全部都是C,C++的特性都没用到过。想想,大概是为了代码上和C兼容性吧。不过既然如此,C效率又高(一家之言,一家之言),为啥不学干脆学C呢?

当然,那里面讲的内容也都比较简单,大概也不需要那些面向对象的编程。

顺带一提,那教材里好多错的说,居然还给印出来了……

终于还是要面对C++的

不仅是C++,还必须得用Visual C++(而且还必须得用6.0)。看来微软在中国高校中的垄断地位一时半会还真无人能撼动啊,这叫私情何以堪。

其实只是做些初步学习,写一般的程序Vim足够用了,Visual C++的作用大概只是用来Debug而已。Eclipse貌似也挺爽的,就是文件体积有点大,而且没有和Python打包在一起的版本(PyDev那个名字古怪的不算),所以以前就没怎么尝试。以后倒是可以试试,对C来说貌似一站式下载就可以了。

想想也是好事。CS Slayer仁兄说在计算机发展到一定境界之前还是很需要这类可以对硬件进行调度的语言,私也有这样的感觉。见识见识总是很好的,说不定以后私就从Python转投C++和Qt了,Qt最初就是给C++绑定的,资料更多更完善。

话说貌似最近网志更新的都很短呢,没办法,实在是挺忙的啊,诸君少假借之吧。