PPA源更新
这几天被ppa源给搞的头晕,其实主要是自己方向不对,经过昨晚的努力终于完整的更新了osd-lyrics版本。记录下过程来备忘。
准备工作:
- 注册一个ppa账户,自己创建一个ppa源或者加入别的ppa团队账户。
- 生成自己的OpenPGP keys,主要是为了给自己维护的deb包签名,具体过程可以参照Introducing OpenPGP keys。
- 生成Code of conduct signatures,在自己账户的页面overview的最下方有Signed Ubuntu Code of Conduct,按照提示操作。
- 修改项目debain目录下的changelog,control,rules等文件,具体请参照:PackagingGuide/Basic
开始更新:
- 用最基本的方式在本地打包:首先确保debuild安装好,然后切换到项目主目录
debuild -S -k<your GPG key ID>sudo pbuilder build ../*.dsc
cd ..
lintian -Ivi *.dsc - 上传到ppa源进行验证:具体配置dput可以参考 Packaging/PPA/Uploading
dput my-ppa *_source.changes
上传之后就god bless you 吧,如果gpg验证通过会发一封accept邮件到你的主email,但不要高兴太早,有可能在ppa服务器上编译有错误,你只能重新打包上传。
ps:编译的时间会很久。。。
linux目录结构
______
___/bin (binary二进制) 常用Linux命令
___/boot 存放着启动Linux时使用的一些核心文件,包括一些链接文件以及镜像文件
___/cdrom 这个目录在你刚刚安装系统的时候是空的。你可以将光驱文件系统挂在这个目录下。
例如:mount /dev/cdrom /cdrom
___/sbin 存放管理员使用的系统管理程序
___/home 存放用户主目录。如果我们建立一个用户,用户名是"xx",
那么在/home目录下就有一个对应的/home/xx路径
___/lib (library库)存放系统动态连接共享库
___/lost+found 一般情况下是空的,当系统非法关机后,这里就存放了一些文件
___/mnt 挂载其它文件系统
___/root 系统管理员(超级用户)主目录
___/shlib 使用运行SCO UNIX程序的iBCS2用的
___/tmp (temporary临时)存放不同程序执行时产生的临时文件,系统会周期性地清除里面的内容
___/vmlinuz 存放的是系统的内核
___/dev (device设备) 该目录下存放的是Linux的外部设备,
在Linux中访问设备的方式和访问文件的方式是相同的
_____/dev/console 系统控制台
_____/dev/fd0 第一个软盘驱动器
_____/dev/hda1 第一个IDE硬盘驱动器的第一个逻辑分区
_____/dev/hdb3 第二个IDE硬盘驱动器的第三个逻辑分区
_____/dev/ttys0 第一个串行端口
_____/dev/lp0 第一个并行端口
_____/dev/cua 用来连接调制解调器
_____/dev/sda 第一个SCSI硬盘驱动器
_____/dev/null 是一个接受设备输入但不产生任何输出的虚拟的设备
_____/dev/tty1 第一个虚拟控制台
_____/dev/pty* “虚拟终端”,用于为远程登录提供终端
___/usr 用户应用程序文件夹
_____/usr/X11R6 存放X-Windows的目录
_____/usr/bin 用户用到的大多数应用程序
_____/usr/sbin 给超级用户使用的比较高级的管理程序和系统守护程序
_____/usr/doc 文档
_____/usr/include Linux下开发或编译应用程序需要用的头文件
_____/usr/openwin 存放SUN的OpenWin
_____/usr/lib 常用的动态、静态库
_____/usr/local 普通用户、超级用户安装新软件使用
_____/usr/man 联机帮助手册
_____/usr/src 系统源代码,(/usr/src/linux系统源代码)
___/etc (etcetera) 系统管理的配置文件和子目录
_____/etc/DIR_COLORS 设定颜色
_____/etc/HOSTNAME 设定用户的节点名
_____/etc/NETWORKING 只有YES标明网络存在
_____/etc/host.conf 文件说明用户的系统如何查询节点名
_____/etc/hosts 设定用户自已的IP与名字的对应表
_____/etc/hosts.allow 设置允许使用inetd的机器使用
_____/etc/hosts.deny 设置不允许使用inetd的机器使用
_____/etc/hosts.equiv 设置远端机不用密码
_____/etc/inetd.conf 设定系统网络守护进程inetd的配置
_____/etc/inetd.pid inetd这个进程的进程id
_____/etc/hosts.lpd 设定远端有哪些节点可以使用本机的打印机
_____/etc/gateways 设定路由器
_____/etc/protocols 设定系统支持的协议
_____/etc/named.boot 设定本机为名字服务器的配置文件
_____/etc/named.pid 本机上运行的名字服务器的进程id
_____/etc/networks 设定网络的配置文件
_____/etc/resolv.conf 设定系统的名字服务器
_____/etc/services 设定系统的端品与协议类型和提供的服务
_____/etc/exports 设定NFS系统用的
_____/etc/NNTP_INEWS_DOMAIN 设置新闻服务器的配置文件
_____/etc/nntpserver 设置用户使用的新闻服务器的地址
_____/etc/XF86Config X Window的配置文件
_____/etc/hostid 系统独有的一个硬件id
_____/etc/at.deny 设置哪些用户不能使用at命令
_____/etc/bootptab 给MAKEDEV程序设定各种不同的设备驱动文件的格式
_____/etc/makedev.cfg 同DEVINFO一样给MAKEDEV使用的设置文件
_____/etc/diphosts 设置拔号服务器的用户名和口令
_____/etc/slip.hosts,/etc/slip.login 设定SLIP的配置文件
_____/etc/fastboot 使用shutdown -f产生的,重启系统要查这个文件
_____/etc/fstab 记录开机要mount的文件系统
_____/etc/mtab 系统在启动时创建的信息文件
_____/etc/ftpaccess FTP服务器的一些配置
_____/etc/ftpconversions 设定在FTP时使用的过滤器的位置
_____/etc/ftpusers 设定不能使用FTP服务的用户
_____/etc/inittab 设定系统启动时init进程将把系统设置成什么样的runlevel
_____/etc/ld.so.cache 查找系统动态链接库的缓存
_____/etc/ld.so.conf 系统动态链接库的路径
_____/etc/lilo.conf lilo的配置文件
_____/etc/magic 给file命令使用的
_____/etc/aliases 给sendmail使用的设置别名的文件
_____/etc/mail.rc,
/etc/mailcap,
/etc/sendmail.cf,
/etc/sendmail.st 设置sendmail的
_____/etc/issue 记录用户登录前显示的信息
_____/etc/motd 超级用户发布通知的地方
_____/etc/organization 存放用户的名字和组织
_____/etc/group 设定用户的组名与相关信息
_____/etc/passwd 用户密码文件(重要呀)
_____/etc/shadow 见/etc/passwd
_____/etc/pnpdevices 列出支持的Plug&Play设备
_____/etc/snooptad 监控用户的屏幕,监听的终端列表
_____/etc/sudoers 可以sudo命令的配置文件
_____/etc/syslog.conf 系统记录程序syslogd的配置文件
_____/etc/utmp 目前在用系统的用户信息
_____/etc/wtmp 同utmp差不多,只是它累加
_____/etc/nologin 系统在shutdown时不希望用户登录就产生这个文件
_____/etc/securetty 设定哪些终端可以让root登录
_____/etc/termcap 设置系统终端信息的
_____/etc/ttys 设定系统的终端类型
_____/etc/gettydefs getty_ps的定义文件
_____/etc/yp.conf NIS的配置文件
_____/etc/mtools.conf 设定mtools程序的参数
_____/etc/fdprm 设定格式化软盘的参数
_____/etc/login.access 控制用户登录权限的文件
_____/etc/login.defs 所有用户登录时的缺省配置文件
___/proc 虚拟系统,内存映象
_____/proc/1 关于进程1的信息目录。每个进程在/proc 下有一个名为其进程号的目录。
_____/proc/cpuinfo 处理器信息,如类型、制造商、型号和性能。
_____/proc/devices 当前运行的核心配置的设备驱动的列表。
_____/proc/dma 显示当前使用的DMA通道。
_____/proc/filesystems 核心配置的文件系统。
_____/proc/interrupts 显示使用的中断
_____/proc/ioports 当前使用的I/O端口。
_____/proc/kcore 系统物理内存映象
_____/proc/kmsg 核心输出的消息。也被送到syslog 。
_____/proc/ksyms 核心符号表。
_____/proc/loadavg 系统"平均负载";3个指示器指出系统当前的工作量。
_____/proc/meminfo 存储器使用信息,包括物理内存和swap。
_____/proc/modules 当前加载了哪些核心模块。
_____/proc/net 网络协议状态信息。
_____/proc/self 到查看/proc 的程序的进程目录的符号连接。当2个进程查看/proc 时, 是不同的连接。这主要便于程序得到它自己的进程目录。
_____/proc/stat 系统的不同状态
_____/proc/uptime 系统启动的时间长度。
_____/proc/version 核心版本。
___/var 存放着那些不断在扩充着的东西,为了保/usr的相对稳定,那些经常被修改的目录可以
放在这个目录下。包括系统一般运行时要改变的数据。
每个系统是特定的,即不通过网络与其他计算机共享。
_____/var/adm
_____/var/catman 当要求格式化时的man页的cache。
man页的源文件一般存在/usr/man/man* 中;有些man页可能有预格式化的版本,
存在/usr/man/cat* 中。而其他的man页在第一次看时需要格式化,
格式化完的版本存在/var/man 中,这样其他人再看相同的页时就无须等待
格式化了。 (/var/catman 经常被清除,就象清除临时目录一样。)
_____/var/lib 系统正常运行时要改变的文件。
_____/var/local /usr/local 中安装的程序的可变数据(即系统管理员安装的程序)。
注意,如果必要,即使本地安装的程序也会使用其他/var 目录,
例如/var/lock 。 /var/lock 锁定文件。
许多程序遵循在/var/lock 中产生一个锁定文件的约定,
以支持他们正在使用某个特定的设备或文件。
其他程序注意到这个锁定文件,将不试图使用这个设备或文件。
_____/var/log 各种程序的Log文件,特别是login (/var/log/wtmp log所有到系统
的登录和注销) 和syslog (/var/log/messages 里存储所有核心和系统程序信息。
/var/log 里的文件经常不确定地增长,应该定期清除。
_____/var/run 保存到下次引导前有效的关于系统的信息文件。例如, /var/run/utmp
包含当前登录的用户的信息。
_____/var/spool mail, news, 打印队列和其他队列工作的目录。
每个不同的spool在/var/spool 下有自己的子目录,
例如,用户的邮箱在/var/spool/mail 中。
_____/var/tmp 比/tmp 允许的大或需要存在较长时间的临时文件。
(虽然系统管理员可能不允许/var/tmp 有很旧的文件。)
(转)GTK+ Widget的内部结构与工作流程
转载一篇TigerSoldier的项目心得,以备以后查看。
http://tigersoldier.is-programmer.com/2009/5/24/gtk-custom-widget-workflow.8478.html
GtkWidget的基本结构是这样的:
GtkStyle *GSEAL (style);
GtkRequisition GSEAL (requisition);
GtkAllocation GSEAL (allocation);
GdkWindow *GSEAL (window);
GtkWidget *GSEAL (parent);
} GtkWidget;
其中最重要的是它的window属性,每个GtkWidget都必须有一个window。Widget是围绕着window转的,只有有了window,Widget的存在才有意义。
要注意这里的window是一个GdkWindow,而不是GtkWindow。GdkWindow是对X的window的封装,大致上是屏幕上的一块矩形区域,可以在上面画画,可以接收事件。
一个Widget从创建、显示到销毁,大致要经过这么几个过程:
1、创建(new)
这是调用gtk_xxx_new时所触发的。它干的活很简单,用gobject的对象系统创建一个相应widget的实例。
当创建实例时,gobject会自动调用指定的初始化(init)函数(在get_type时指定),init函数负责把widget的各字段都初始化(把标题文字什么的设为NULL之类的)。
注意此时window并没有被创建,其实只是有了个widget的架子而已。
创建之后就可以对widget进行各种属性的设置了。
2、实例化(realize)
实例化的过程,就是将window创建出来的过程。这其中包括几个阶段:
询问大小请求(size_request)
GTK在实例化一个widget之前,会询问这个widget希望的大小是多大。widget可以根据自己的情况(例如属性什么的),计算出自己所需要的大小,也可以返回一个默认值,反正就是widget自己定啦:)。
分配大小(size_allocate):
GTK获得大小请求后就会给widget分配一个大小。要注意的是分配的大小不一定和请求的大小相同。一般来说,在分配大小时widget需要做几件事。
将分配的大小记录在自己的allocation中。
如果自己的window已经创建了,那么要改变自己所拥有的window的大小,使之符合所分配的大小。
如果widget是一个容器(container),那么对其所有的子widget也要相应地计算它们的大小并重新给它们分配大小。
分配大小可能发生在实例化之前,也可能在实例化后因为所属容器的大小、位置发生变化而被重新分配,因此widget的window可能已经被创建,也可能是NULL,需要进行判断。
实例化
这才是真正的实例化阶段。实例化所需要做的事只有一个:用gdk_window_new创建window。创建好window后需要用 GTK_WIDGET_SET_FLAGS来给widget设置GTK_REALIZED标志。设置之后用GTK_REALIZED宏检查widget是 否已经被实例化时会返回TRUE,表示该widget已经被实例化了。
可以用gtk_widget_realize手动实例化一个widget
3、映射(map)
所谓映射,就是将已经创建好的window映射(显示)到屏幕上。需要做的事是用gdk_window_show将window给显示出来。和实例化时类似,需要用GTK_WIDGET_SET_FLAGS设置GTK_MAPPED标志,表示已经映射好了。
要注意的是map时需要判断widget是否已经实例化(用GTK_REALIZED),如果没有,应该首先实例化widget,这样才能显示window。
同样可以用gtk_widget_map手动映射一个widget。
用gtk_widget_show来显示一个widget的本质,就是将widget实例化,并将其映射。当然每一步都要判断是否已经做过,重复实例化和映射会造成资源泄漏(window被多次创建)和其他问题。
以上就是一个widget从创建到显示的过程。当然其中还有其父widget的流程。一个widget当且仅当其父widget被实例化后才能实例化,映射亦然(放心,这个流程是GTK+自动判断的)
接下来就是销毁一个widget时要做的事了。
4、反映射(unmap)
当隐藏一个widget时,其实就是取消这个widget的映射。具体做法是用gtk_window_hide来隐藏window,并用GTK_WIDGET_UNSET_FLAGS来取消(GTK_MAPPED)。
5、反实例化(unrealize)
销毁一个widget之前会自动要求将其反实例化。反实例化就是将window给销毁(记得把window指针设回NULL),并取消(GTK_REALIZED)标志。
有时可能会需要用gtk_widget_unrealize来手动反实例化一个widget。
6、销毁(destroy)
和new对应,把剩下的资源释放,最后用gobject的相应函数释放整个widget
下面是取自GtkEntry中的典型代码:
创建:
gtk_entry_new (void)
{
/* 返回类型为GTK_TYPE_ENTRY的对象(Gobject的工作) */
return g_object_new (GTK_TYPE_ENTRY, NULL);
}
/* 初始化函数,在g_object_new时自动调用 */
static void
gtk_entry_init (GtkEntry *entry)
{
GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry);
/* 设置widget标识 */
GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
/* 初始化各字段 */
entry->text_size = MIN_SIZE;
entry->text = g_malloc (entry->text_size);
entry->text[0] = '\0';
/* …… */
/* 设置拖放 */
gtk_drag_dest_set (GTK_WIDGET (entry),
GTK_DEST_DEFAULT_HIGHLIGHT,
NULL, 0,
GDK_ACTION_COPY | GDK_ACTION_MOVE);
gtk_drag_dest_add_text_targets (GTK_WIDGET (entry));
/* 输入法context */
entry->im_context = gtk_im_multicontext_new ();
/* 信号 */
g_signal_connect (entry->im_context, "commit",
G_CALLBACK (gtk_entry_commit_cb), entry);
/* …… */
}
大小分配:
gtk_entry_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GtkEntry *entry = GTK_ENTRY (widget);
/* 保存到allocation中 */
widget->allocation = *allocation;
/* 判断是否实例化 */
if (GTK_WIDGET_REALIZED (widget))
{
/* 计算窗口大小…… */
/* 改变窗口大小 */
gdk_window_move_resize (widget->window, x, y, width, height);
/* …… */
}
}
大小请求:
gtk_entry_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
/* 计算所需大小…… */
/* 设置所城大小 */
if (entry->width_chars < 0)
requisition->width = MIN_ENTRY_WIDTH + xborder * 2 + inner_border.left + inner_border.right;
else
{
/* …… */
requisition->width = char_pixels * entry->width_chars + xborder * 2 + inner_border.left + inner_border.right;
}
requisition->height = PANGO_PIXELS (entry->ascent + entry->descent) + yborder * 2 + inner_border.top + inner_border.bottom;
/* …… */
}
实例化:
gtk_entry_realize (GtkWidget *widget)
{
/* …… */
/* 设置标志 */
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
/* …… */
/* 创建window */
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
gdk_window_set_user_data (widget->window, entry);
/* …… */
}
映射:
gtk_entry_map (GtkWidget *widget)
{
/* …… */
/* 判断是否可以且需要显示 */
if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_MAPPED (widget))
{
/* 调用父类的map函数,也就是GtkWidget的,这样就不用自己设置GTK_MAPPED和显示widget->window了 */
GTK_WIDGET_CLASS (gtk_entry_parent_class)->map (widget);
/* …… */
/* 显示需要显示的window */
gdk_window_show (icon_info->window);
/* …… */
}
反映射:
gtk_entry_unmap (GtkWidget *widget)
{
/* …… */
/* 判断是否需要隐藏 */
if (GTK_WIDGET_MAPPED (widget))
{
/* …… */
/* 隐藏需要显示的window */
gdk_window_hide (icon_info->window);
/* …… */
/* 调用父类的unmap函数,也就是GtkWidget的,这样就不用自己取消GTK_MAPPED和隐藏widget->window了 */
GTK_WIDGET_CLASS (gtk_entry_parent_class)->unmap (widget);
}
}
反实例化:
gtk_entry_unrealize (GtkWidget *widget)
{
/* …… */
/* 调用父类的unrealize函数来销毁widget->window和取消GTK_REALIZED标识 */
GTK_WIDGET_CLASS (gtk_entry_parent_class)->unrealize (widget);
/* …… */
}
销毁:
gtk_entry_destroy (GtkObject *object)
{
/* 销毁为成员分配的空间…… */
/* 用父类的object销毁函数自动调用gobject来销毁 */
GTK_OBJECT_CLASS (gtk_entry_parent_class)->destroy (object);
}
GUN Emacs常用快捷键
基本命令
C-x C-f : 打开一个文件,如果文件不存在,则创建一个文件
C-g : 取消未完成的命令
编辑
C-x u 是默认的命令; 移动一下光标,再C-z就可以redo
M-d : 删除光标后的词语
移动光标
M-v : 向后翻页
M-r : 将光标移动到屏幕中间那行
C-a : 移到行首
M-a : 移到句首,从行首到句首之间可能有空格
C-e : 移到行尾
M-e : 移到句尾
M-{ : 向上移动一段
M-} : 向下移动一段
C-right : 向前移动一个单词
C-left : 向后移动一个单词
C-up : 向前移动一段
C-down : 向后移动一段
M-< : 移到整个文本开头
M-> : 移到整个文本末尾
C-u 数字 命令 : 执行多次(数字表示次数)该命令;“M-数字 命令” 也可以
M-x goto-line : 移动到某一行
C-l : 重绘屏幕,效果就是当前编辑行移动窗口中央
Buffer 相关
C-x b : 切换到前一个编辑的buffer
C-x C-b : 列出当前所有buffer
C-x C-s : 保存当前buffer
C-x s : 保存所有未保存的buffer,会提示你是否需要保存
C-x C-w : 文件另存为
拷贝与粘贴
C-w (redefined) : 剪切一块区域;如果没有设置mark,则是剪切一行
M-w (redefined) : 拷贝一块区域;如果没有设置mark, 则是拷贝一行
C-k : 从当前位置剪切到行尾
C-y : 粘贴
M-y : 用C-y拉回最近被除去的文本后,换成 M-y可以拉回以前被除去的文本。键入多次的M-y可以拉回更早以前被除去的文本。
C-x r k : 执行矩形区域的剪切
C-x r y : 执行矩形区域的粘贴
窗口操作
C-x 1 : 将当前窗口最大化
C-x 2 : 垂直分割窗口
C-x 3 : 水平分割窗口
M-o (redefined) : 在窗口之间切换; C-x o 是默认命令
C-x 5 1/2/3/0 : 对frame类似的操作
C-x < : 窗口内容右卷
C-x > : 窗口内容左卷(这两个命令在垂直分割窗口后比较有用)
(C-u) C-x ^ : 加高当前窗口,如果有C-u,则每次加高4行
(C-u) C-x } : 加宽当前窗口
(C-u) C-x { : 压窄当前窗口
ESC C-v : 在其它窗口进行卷屏操作
搜索和替换
C-s RET : 普通搜索
C-r : 向前搜索
C-s RET C-w : 按单词查询
M-% : 查询替换,也就是替换前会询问一下
M-x replace-string : 普通替换
Tags
M-. : 跳到tag所在位置
M-x list-tags : 列出tags
Bookmark
C-x r b : 跳到bookmark处
帮助
C-h f : 查看一个函数
C-h v : 查看一个变量
C-h k : 查看一个键绑定 (C-h c 也是查看键绑定,但是信息较简略)
C-h C-f : 查看一个函数的Info,非常有用
C-h i : 看Info
其它
C-x h : 全部选中
M-! : 执行外部shell命令
M-x shell : 模拟shell的buffer
M-x term : 模拟terminal, C-c k 关闭terminal
C-x C-q : 修改buffer的只读属性
浅析GLib
最近在和室友做一个开源项目,主要用GTK+实现。所以对GTK+的基础Glib产生兴趣,在网上寻找资料的时候发现这篇文章,特此转载,以备不时之需。
GLib是GTK+和GNOME工程的基础底层核心程序库,是一个综合用途的实用的轻量级的C程序库,它提供C语言的常用的数据 结构的定义、相关的处理函数,有趣而实用的宏,可移植的封装和一些运行时机能,如事件循环、线程、动态调用、对象系统等的API。它能够在类UNIX的操 作系统平台(如LINUX, HP-UNI等),WINDOWS,OS2和BeOS等操作系统台上运行。
GLib需要一个支持线程的操作系统和一个字符集间转换函数iconv的支持,事实上大多现代的操作系统都有以上两项功能。GLib由基础类型、对核心应用的支持、实用功能、数据类型和对象系统五个部分组成的。可以到www.gtk.org网站下载GLib其源代码。使用GLib2.0编写的应用程序,在编译时应该在编译命令中加入 `pkg-config -cflags -libs glib-2.0`,如编译一个名为hello.c的程序,输出名为hello的可执行文件,则命令为:
在GLIB中将线程(gthread),插件(gmoudle)和对象系统(gobject)这三个子系统区别对待,编译时要注意加入相应的参数。如程序中用到对象系统,编译时就应加入:
用到线程,编译时则加入:
用到插件,编译时则加入:
基础类型:
GLib的基础是由基础类型、范围限定宏、标准宏、类型转换宏、字节次序变换宏、数学常数定义和杂项宏等各项组成的。这里主要介绍基础类型,因为它们遍及与GLIB相关的各种程序库和软件包中,如GTK+,GNOME,MONO等大的开源项目。
基础类型又称标准类型,GLib将C语言中的数据类型统一封装成自己的数据类型,均以小写字母'g'开头,如:gpointer是指针类型(void *)、guint是无符号整型(unsigned int)等,其中有一些是修饰性的,如:gint、gchar等,它们和C语言中的int、char是完全相同的。这些数据类型使用起来和C语言中的数据 类型完全一样,当你熟悉了以后会发现它们的使用方法更灵活,更直观也更易于理解一些。当然你可以把C语言中的数据类型直接拿来使用,这丝毫不影响你编写程 序的编译。
另外范围限定宏和类型转换宏也较常用,如G_MAXINT表示最大的int型值,用宏GINT_TO_POINTER(i)将整型变量i转换为指针型,宏GPOINTER_TO_INT(p)将指针型变量p转换为整型。
逻辑类型gboolean的值TRUE和FALSE是在常数宏中定义的,另外还包括G_E表示自然对数,G_PI表示圆周率,G_PI_2表示圆周率的1/2等一些数学常数。
对核心应用的支持:
GLib对核心应用的支持包括事件循环、内存操作、线程操作、动态链接库的操作和出错处理与日志等。下面代码演示了事件循环、内存操作、线程这三种功能的简单应用:
static GMutex *mutex = NULL;
static gboolean t1_end = FALSE;
static gboolean t2_end = FALSE;
typedef struct _Arg Arg;
struct _Arg
{
GMainLoop* loop;
gint max;
};
void run_1(Arg *arg)
{
int i ;
for(i=0; i<arg->max; i++)
{
if(g_mutex_trylock(mutex) == FALSE)
{
//g_print("%d : thread 2 locked the mutex \n", i);
g_print("%d :线程2锁定了互斥对象\n", i);
g_mutex_unlock(mutex);
}
else
{
g_usleep(10);
}
}
t1_end = TRUE;
}
void run_2(Arg *arg)
{
int i;
for(i=0; i<arg->max; i++)
{
if(g_mutex_trylock(mutex) == FALSE)
{
//g_print("%d : thread 1 locked mutex \n", i);
g_print("%d :线程1锁定了互斥对象\n", i);
g_mutex_unlock(mutex);
}
else
{
g_usleep(10);
}
}
t2_end = TRUE;
}
void run_3(Arg *arg)
{
for(;;)
{
if(t1_end && t2_end)
{
g_main_loop_quit(arg->loop);
break;
}
}
}
int main(int argc, char *argv[])
{
GMainLoop *mloop;
Arg *arg;
if(!g_thread_supported())
g_thread_init(NULL);
mloop = g_main_loop_new(NULL, FALSE);
arg = g_new(Arg, 1);
arg->loop = mloop;
arg->max = 11;
mutex = g_mutex_new();
g_thread_create(run_1, arg, TRUE, NULL);
g_thread_create(run_2, arg, TRUE, NULL);
g_thread_create(run_3, arg, TRUE, NULL);
g_main_loop_run(mloop);
g_print("线程3退出事件循环\n");
g_mutex_free(mutex);
g_print("释放互斥对象\n");
g_free(arg);
g_print("释放参数所用的内存\n");
}
Makefile文件如下:
all:
$(CC) `pkg-config --cflags --libs glib-2.0 gthread-2.0` loop.c -o loop
下面为输出结果:
1 :线程2锁定了互斥对象
2 :线程1锁定了互斥对象
3 :线程2锁定了互斥对象
4 :线程1锁定了互斥对象
5 :线程2锁定了互斥对象
6 :线程1锁定了互斥对象
7 :线程2锁定了互斥对象
8 :线程1锁定了互斥对象
9 :线程2锁定了互斥对象
10 :线程1锁定了互斥对象
线程3退出事件循环
释放互斥对象
释放参数所用的内存
以上例程创建了三个线程,其中run_1和run_2操作互斥对象,run_3检索前两个线程是否结束,如结束的话,则执行g_main_loop_quit退出事件循环。由于线程的运行是不确定的,所以不一定每次都是这一输出结果。
首先定义一个结构类型来保存创建的事件循环的对象指针和线程运行时的最多循环次数,一般情况下,如果为此数据结构来分配内存的话,用Arg *arg = (Arg *)malloc(sizeof(Arg));,释放时用free(arg);,这种传统的做法曾经让很多C语言的初学者头痛,尤其是需要多次操作的时 候,GLib中提供了类似的函数g_malloc和g_free,最好用的方法是其将g_malloc函数封装成了宏g_new,这个宏有两个参数,第一 个是结构类型,第二个是要分配结构的数量,这段代码中只用到了一个Arg数据结构,所以是g_new(Arg, 1)。在程序结束时用g_free来释放。
在线程初始化时,首先是判断线程是否初始化的函数g_thread_supported,如果其返回FALSE则表明线程并未初始化,这时必须用g_thread_init来初始化,这是较明智的做法。
事 件循环GMainLoop在用g_main_loop_new创建之后并不马上运行,用g_main_loop_run运行后,还要用 g_main_loop_quit退出,否则循环将一直运行下去,这两个函数的参数都是GMainLoop型的指针,在主函数中并未直接运行 g_main_loop_quit,而是把它放在线程的函数中了,这一点需读者注意。
实用功能:
GLib中包含了近二十种实用功能,从简单的字符处理到初学者很难理解的XML解析功能,这里介绍两种较简单的:随机数和计时。下面代码演示如何产生1-100之间的随机整数和演示如何计算30000000次累加在计算时用的时间:
#include <glib.h>
int main(int argc, char *argv[])
{
GRand *rand;
GTimer *timer;
gint n;
gint i, j;
gint x = 0;
rand = g_rand_new(); //创建随机数对象
for(n=0; n<20; n++)
{ //产生随机数并显示出来
g_print("%d\t",g_rand_int_range(rand,1,100));
}
g_print("\n");
g_rand_free(rand); //释放随机数对象
//创建计时器
timer = g_timer_new();
g_timer_start(timer);//开始计时
for(i=0; i<10000; i++)
for(j=0; j<3000; j++)
x++;//累计
g_timer_stop(timer);//计时结束
//输出计时结果
g_print("%ld\tall:%.2f seconds was used!\n",x,g_timer_elapsed(timer,NULL));
}
Makefile文件内容如下:
输出结果:
30000000 all:1.47 seconds was used!
GLIB中的每个对象几乎都有一个或多个*_new函数来创建,计时器GTimer和随机器GRand也一样,也都有相对应的函数来结束对象的使用,如GTimer的g_timer_stop和GRand的g_rand_free。
这可能是GLIB实用功能中最简单的两种了,许多朋友会一目了然。我们还应注意到GLIB的代码风格和封装技巧是具有独到之处的,这种风格和技巧足以让一些自称简洁实用的SDK汗颜,学习掌握这一风格可能会让我们受益匪浅。
数据类型:
GLib中定义了十几种常用的数据结构类型和它们的相关操作函数,下面是关于字符串类型的简单示例:
int main(int argc, char *argv[])
{
GString *s;
s = g_string_new("Hello");
g_print("%s\n", s->str);
s = g_string_append(s," World!");
g_print("%s\n",s->str);
s = g_string_erase(s,0,6);
g_print("%s\n",s->str);
s = g_string_prepend(s,"Also a ");
g_print("%s\n",s->str);
s = g_string_insert(s,6," Nice");
g_print("%s\n",s->str);
}
Makefile文件如下:
all:
$(CC) `pkg-config --cflags --libs glib-2.0 ` string.c -o str
下面是输出结果:
Hello World!
World!
Also a World!
Also a Nice World!
字符串在编程中出现频率之高,即使是初学者也很清楚,追加、删除和插入等常用操作理解后,还可以进一步了解掌握其它更复杂的操作。
GLib提供了一种内存块(GMemChunk)数据类型,它为分配等大的内存区提供了一种非常好用的操作方式,下面程序演示了内存块数据类型的简单用法:
int main(int argc, char *argv[])
{
GMemChunk *chunk; //定义内存块
gchar *mem[10]; //定义指向原子的指针数组
gint i, j;
//创建内存块
chunk = g_mem_chunk_new("Test MemChunk", 5, 50, G_ALLOC_AND_FREE);
//名称,原子的长度, 内存块的长度,类型
for(i=0; i<10; i++)
{
//创建对象
//mem[i] = g_chunk_new(gchar, chunk);
mem[i] = (gchar*)g_mem_chunk_alloc(chunk);
for(j=0; j<5; j++)
{
mem[i][j] = 'A' + j;//为内存块中的指针赋值
}
}
g_mem_chunk_print(chunk); //显示内存块信息
for(i=0; i<10; i++)
{
g_print("%s\t",mem[i]);//显示内存块中的内容
}
for(i=0; i<10; i++)
{
g_mem_chunk_free(chunk,mem[i]); //释放所有分配的内存
}
g_mem_chunk_destroy(chunk);
}
Makefile文件件如下:
all:
$(CC) `pkg-config --cflags --libs glib-2.0` data1.c -o data1
以下为输出结果:
ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE
这里说明这一数据类型的原因是通过它可以他细体会内存分配这一运行时处理环节的应用之妙。我们在程序中分配的是50字节的空间,而实际用的是80字节,由此可以看出其在分配内存时本身用到了部分内存空间。
从上面的示例代码中可以看出,在GLib中几乎所有的对象都是C语言的结构类型,一般命名以大写字母G开头的单词,如GList表示双向链表,所有与之相关 的操作函数都以小写的字母g加下划线加小写的单词加下划线开头,如以g_list_*开头的函数都是与这相关的操作函数,而且这些函数中的第一个参数多数 是此对象的指针。
GLIB中的数据类型在GLIB本身,尤其是GTK+中频繁用到,了解掌握这些数据类据类型的用法是非常必要的,这对进一步灵活开发GTK+程序来说是关键一环,而且是对大学中的《数据结构》一科的很好回顾。
GLib的在线文档: http://developer.gnome.org/doc/API/2.0/glib/index.html
加入is-programmers
最近看了宿舍大牛TigerSoldier的blog,立刻被它的简洁、专业所吸引,所以决定以后就在这里混了,哈哈~