Dear X users, 由於陆续有网友写 mail 来索取程式, 所以我决定将文章及程式 一并发表在网上的 X Window 讨论群上,本文的著作权为本人所拥有, 未经本人同意,将本文转载於其他刊物或做修改後发表,均已构成侵权 行为,请各位留意,谢谢! 本文主旨在讨论 X Window 的 Property,并实地设计了两支程式 来探讨 X Window Property 的运作,这机制是 X Window 程式设计中 稍微难一点的主题,不过还是希望对有兴趣的读者有所帮助。 我把文章先行列出,程式随後,祝各位研读愉快! 陈重嘉 A maina of X & Unix X 视窗的资料共享:性质 关键字: X 视窗系统 X window system X 伺服器 X server 机制 mechanism 性质 property 原子 atom 视窗识别码 window id 导言 在 unix 系统中,不同的应用程式要共享或交换资料,可以透过像 pipe,share memory 等内部程序沟通(InterProcess Communication) 的机制来达成;而在 X 视窗系统中,也提供了类似 unix 内部程序沟 通的机制,使得不同 X 视窗的应用程式可以共享或交换资料,这机制 就是 X 视窗系统中的性质(property)。打个比方来说,我们可以把 X 视窗的性质看做是一个可以装填资料的容器,这个容器标明了一个名字 与其内含值的资料型态,并且将这个容器放在相关应用程式都知道的 地方,於是这些相关的应用程式就可透过这个容器,来达成资料共享或 资料交换的目的。本文即就 X 视窗的性质机制作一番研讨,并设计两支 X 视窗的应用程式,来验证透过性质的机制可使不同的应用程式共享资料。 性质与原子 在导言中提到过,性质必须有名字(name)及内含值的资料型态(data type) ,这两者都是以可变长度的字元串来定义的,例如定义一个性质的名字 (name)为「bdc」,其资料型态(data type)为「bdc_type」。应用程式 可以自行定义性质的名字与内含值的资料型态,然後再分别将它们转换 为原子,也可以使用 X 视窗预先为我们定义好的性质名字的原子与资料 型态的原子,<X11/Xatom.h> 标头档中就包含了这些定义好的原子, 其起始字元串皆为「XA_」,如 XA_STRING,XA_INTEGER 等。而什麽 是原子呢?我们已经知道性质的名字与内含值的资料型态是以可变长度 的字元串来定义的,但是在 X 伺服器与应用程式之间,藉由传送整个 字元串来指定性质的名字与内含值的资料型态是没有效率且浪费网路 频宽的;因此,X 视窗系统以另一个三十二位元的识别码(32-bit id) 来表示这个字元串,这三十二位元的识别码就是「原子」(atom)。在 同一个 X 伺服器上,每一个原子都是唯一的(unique),不会有两个 原子是相同的。 为性质命名 应用程式可以使用 X 视窗预先定义的性质名字的原子,如果应用程式 不打算使用预先定义的性质名字的原子,可以自行定义性质的名字, 然後将它转换为原子。当应用程式为一个性质取定一个名字後,接著 便是将这个名字转换为 X 伺服器看得懂的原子,转换的工作可藉由呼叫 XInternAtom 来达成,其函式的型式如下: Display *display; char property_name[]; Bool only_if_exists; Atom atom; atom = XInternAtom(display, property_name, only_if_exists); display 为应用程式和 X 伺服器的连线(connection),通常 X 视窗的 应用程式一开始就会建立这条连线。 property_name 为性质的名字,当应用程式要产生一个新的性质时, 指定 False 给 only_if_exists;当性质已经存在且已悬挂在某视窗上, 则指定 True 给 only_if_exists,此时 XInternAtom 会传回该性质名字 相对应的原子;但是如果此性质已经不存在,而应用程式又指明 only_if_exists 之值为 True,此时 XInternAtom 的传回值为 None。在为性质命名时,必须 注意名字的大小写是有分别的,如「thing」和「Thing」,分别代表两个不同 性质的名字。 如果 XInternAtom 在执行中发生错误,则其传回值为 BadAlloc 或 BadValue。 一个应用程式产生一个新的性质,并将它转换为相对应的原子後,即使当初 产生该性质的应用程式结束,其性质相对应的原子的定义依然有效,除非 X 伺服器整个结束掉,这个原子的定义才会被释放。所以 X 视窗的应用程式 应该尽量避免使用过量的原子,以节省 X 视窗的系统资源。 指明性质内含值的资料型态 性质内含值的资料型态(data type)是以可变长度来定义的,应用程式可 以使用 X 视窗预先定义的资料型态的原子,也可以自行定义一个字元 串来表示一个资料型态,然後将它转换为原子,这项转换工亦是透过呼 叫 XInternAtom 来达成。举例来说,如果应用程式使用预先定义的资 料型态原子,如 XA_STRING,则应用程式便可免掉呼叫 XInternAtom 的工作,直接使用预先定义的资料型态原子;但是如果应用程式自行定义 了一个性质的资料型态为「bdc_type」,则应用程式必须呼叫 XInternAtom 函式将「bdc_type」这个字元串转换为原子,以便和 X 伺服器沟通,其 程式码如下: Display *display; Atom prop_type; prop_type = XInternAtom(display, "bdc_type", False); 性质的资料格式 每一个的性质都有一个资料格式,其格式的参数值为 8、16 或 32 , 分别表示性质内的资料是以八、十六或三十二位元来储存。 将性质挂在视窗上并存入资料 在一般的情况下,不同的应用程要共享、交换资料,最方便的方式是 将性质挂在根视窗(root window)下,因为每一个 X 伺服器都有一个 根视窗,这个根视窗是 X 视窗应用程式「都知道的地方」,透过 DefaultRootWindow 这个巨集呼叫,应用程式可以取得根视窗的视窗 识别码。另一方式是把性质挂在某个非根视窗的视窗上,而要共享、 交换资料的相关的应用程式「都要知道这个视窗」,如此才能把资料 存到这个视窗上的性质中,或从这个视窗上的性质中读取资料。藉由 呼叫 XChangeProperty 这个函式,可以将性质挂在视窗上,并将资料 存入性质中, 其函式的型式如下: Display *display; Window window; Atom prop_name; Atom prop_type; int prop_format; int prop_mode; unsigned char *data; int nbytes; XChangeProperty(display, window, prop_name, prop_type, prop_format, prop_mode, data, nbytes); display 为应用程式和 X 伺服器的连线(connection),通常 X 视窗的 应用程式一开始就会建立这条连线。 window 为性质欲悬挂的视窗的视窗识别码。 prop_name 为性质名字的原子。 prop_type 为性质资料型态的原子。 prop_format 指明资料将以何种格式存放於性质内,其允许值为 8、16 或 32,如果是 16 或 32,则 XChangeProperty 中的第七个参数 data 必须做 (char *) 的型态转换(type casting)。prop_format 内的资讯使得 X 伺服器 在必要的时候可以做 byte swap operation。 prop_mode 指明资料的储存方式,其值定义於 <X11/X.h> 中,分述如下: 一、 PropModeReplace 表示性质中的原有资料完全被新的资料所覆盖。 二、 PropModePrepend 表示新的资料是附加於性质中的原有资料的前面。 三、 PropModeAppend 表示新的资料是附加於性质中的原有资料的後面。 data 即为应用程式所要存放於性质中的资料,为一字元指标。 nbytes 指明 data 中有多少个 byte。 XChangeProperty 用来将资料存入悬挂在视窗上的一个性质中,如果指定的 性质不存在, 则 XChangeProperty 会自动地产生这个性质。 此外,呼叫 XChangeProperty 会产生一个 PropertyNotify 事件给悬挂该性质的视窗。 性质一旦被应用程式所定义,即使当初定义该性质的应用程式结束,该性质的 定义依然有效,除非有下列三种情况之一发生,该性质的定义才会被释放,这 三种情况是: 一、当初定义该性质的应用程式,呼叫 XDeleteProperty 函式删除该性质的 定义。 二、悬挂该性质的视窗已被关掉(已不存在)。 三、和 X 伺服器的连线(connection)已被结束掉。 性质内能存放资料的大小是和伺服器相关的(server-dependent),如果伺服器上 的记忆体空间够大的话,可以调整性质内能存放资料的空间大小。 由视窗上的性质中读取资料 当应用程式知道某个视窗上有某个性质时,可以透过呼叫 XGetWindowProperty 这个函式去读取该视窗上该性质内的资料,其函式的型式如下: Display *display; Window window; Atom prop_name; long offset; long length; Bool delete; Atom prop_type; Atom ret_prop_type; int ret_format; unsigned long ret_length; unsigned long bytes_left; unsigned char *ret_prop; XGetWindowProperty(display, window, prop_name, offset, length, delete, prop_type, &ret_prop_type, &ret_format, &ret_length, &bytes_left, &ret_prop); display 为应用程式和 X 伺服器的连线(connection),通常 X 视窗的 应用程式一开始就会建立这条连线。 window 为悬挂该性质的视窗的视窗识别码。 prop_name 为该视窗上该性质名字相对应的原子。 offset 指明应用程式要从那个起始位置读取资料。 length 指明应用程式一次要读取多少笔资料。。 delete 表示应用程式在读取性质内的资料後,要不要删除该性质,其值可为 True 或 False,当 delete 之值为 False 时,表示应用程式在读取完性质内的 资料後,不会去删除该性质;当 delete 之值为 True 时,表示应用程式在读取 完该性质内的资料後,如果 bytes_left 的值为零(表示性质内已无尚未读取的 资料),则删除该视窗上的性质,并产生一个 PropertyNotify 事件给该视窗。 prop_type 表示应用程式所期望的性质是哪一种资料型态,以原子来表示。 ret_prop_type 表示该视窗上该性质之真正的资料型态,以原子来表示,此参数 是结果回传值。 ret_format 表示该视窗上该性质内的资料格式,可能的值为 8、16 或 32,此 参数是结果回传值。 ret_length 表示该性质内的资料,如果以 ret_format 的单位来计算,总共有多 少笔,此参数是结果回传值。 bytes_left 表示应用程式在执行一次读取该性质内的资料後,性质内还有多少 尚未被读取的资料;如果性质内还有尚未被读取的资料,应用程式可以再呼叫 XGetWindowProperty 函式去撷取尚未被读取的资料,此参数是结果回传值。 ret_prop 为指向该性质内资料的一个字元指标,此参数是结果回传值。 如果应用程式指明的性质不在该视窗上,则该函式呼叫完後, ret_prop_type 之值 为 None, ret_format 之值为零, bytes_left 之值为零,ret_length 之值为零。如果应用程式指明的性质的确悬挂在该视窗上,但是应用程式所期望 的性质资料型态和该性质的真正资料型态不一致时,则该函式呼叫完後, ret_prop_type 表明性质真正的资料型态, ret_format 表明性质内的资料格式 (绝不会为零), bytes_left 表明该性质内有多少个位元组(byte)的资料(即使 ret_format 的值为 16 或 32), ret_length 之值为零。如果应用程式指明的性质 的确悬挂在该视窗上且应用程式所期望的性质资料型态和该性质的真正资料型态一致 ,或该性质的真正资料型态为 AnyPropertyType 时,则在该函式呼叫完後, ret_prop_type 表明性质真正的资料型态, ret_format 表明性质内的资料格式 (绝不会为零),而 ret_length 及 bytes_left 的计算公式如下: N = 表示性质内的资料,以位元组来计算的话有多少笔。 I = 4 * offset (将单位由 long 转换为 byte) L = min( (N-I) , 4 * length ),如果 L 小於零,则为 BadValue,如果 L 大於或等於零,则将 L 除以 ret_format 後的值指定给 ret_length。 bytes_left = N - (I+L) 综合上述,可以归纳出: 一、如果 ret_format 的值为零的话,表示该视窗上没有应用程式所指明的性质。 二、如果 ret_format 的值不为零,但是 ret_length 的值为零,表示应用程式 所期望的性质资料型态与悬挂在该视窗上性质的真正资料型态不一致。 三、如果 bytes_left 的值不为零,表示该性质中还有尚未被取的资料,应用程式 应该分段地把性质内的资料全部读取出来。 当 XGetWindowProperty 执行成功时,传回值为 Success(其值为零),失败时 传回值为 1 。 删除性质的定义 如果应用程式打算删除性质的定义,可呼叫 XDeleteProperty 来完成这项工作, 其函式的型式如下: Display *display; Widnow window; Atom prop_name; XDeleteProperty(display, window, prop_name); display 为应用程式和 X 伺服器的连线(connection),通常 X 视窗的 应用程式一开始就会建立这条连线。 window 为悬挂该性质的视窗的视窗识别码。 prop_name 为应用程式要删除的性质名字的原子。 此函式执行成功後,会送出 PropertyNotify 事件给原来悬挂该性质的 视窗。 什麽时候会产生 PropertyNotify 事件 在 X 视窗函式呼叫中,共有四个函式呼叫会产生 PropertyNotify 事件, 这四个函式为: 一、 XChangeProperty 二、 XDeleteProperty 三、 XGetWindowProperty 四、 XRotateWindowProperty 因此,当应用程式叫用其中一个系统呼叫时,需记得其已产生了一个 PropertyNotify 事件,而选择此事件的事件遮罩(event mask)为 PropertyChangeMask,应用程式可藉由呼叫 XSelectInput 函式来选择 PropertyNotify 事件。 对 PropertyNotify 事件执行相对应的运作程序 在 X 视窗系统中,所有的运作都是根据事件发生的与否来决定要不要 执行相对应的运作程序(operation procedure),即所有的运作都是 由事件来导动的(event-driven)。在 X 视窗的系统呼叫中,部份的系 统呼叫会产生特定的事件而後送给特定的视窗,应用程式必须自己决定 是否对这些事件做出必要的反应。举例来说,在前面所介绍的 X 系统 呼叫中, XChangeProperty 就会产生一个 PropertyNotify 事件,送给 悬挂该性质的视窗,此举在通知相关的应用程式,悬挂在该视窗上的性质 内的资料已被更改或性质已被删除,接下来的就是这些相关的应用程式 必须决定当 PropertyNotify 事件发生时,要不要执行相对应的运作 程序,而这相对应的运作程序是由应用程式定义的。因此,如果应用程式 想知道某视窗上的性质内的资料是否被更动,必须在该视窗的事件回圈 中选择 (select or solicit) PropertyNotify 事件,以便当 PropertyNotify 事件发生时,能执行其所定义的运作程序。 PropertyNotify 事件的结构 PropertyNotify 事件的结构定义如下: typedef struct { int type; unsigned long serial; Bool send_event; Display *display; Widnow window; Atom atom; Time time; int state; } XPropertyEvent; typedef union _XEvent { ... XPropertyEvent xproperty; ... } XEvent; XPropertyEvent 的各栏位意义如下: type 为发生的事件型态。 serial 为上一个被服务的请求的序列编号(serial number),此乃因 X 视窗系统将 应用程式送给 X 伺服器的所有请求做了序列化处理(serialization),以 避免在存取系统资源时发生竞争现象。 display 为指向 X 伺服器的连线(connection)。 window 为悬挂该性质视窗的视窗识别码。 atom 为该性质名字的原子。 time 表示性质内的资料被更动时,X 视窗的系统时间。 state 表示性质的变化状态,其值有二,分别是 一、PropertyNewValue 表示性质内的资料是一个新值。 二、PropertyDeleted 表示性质的定义已被删除。 程式说明 程式的发展平台为 Solaris 2.4, Solaris Software Developer Kit, X11R5, Motif 1.2.3 run time environment。 □例程式有两支,分述如下: 【 put_to_prop.c 】 此程式主要大纲为:产生一个新的性质,将这性质悬挂在根视窗(root window)下,由 text widget 中读取资料後,将这资料存放到悬挂在根视窗的性质中。程式一开始 执行 X 视窗应用程式的初始化工作,以产生一个 toplevel widget,接著分别呼叫 XtDisplay 及 DefaultRootWindow 以取得应用程式和 X 伺服器的连线及根视窗的 视窗识别码,这两个都是呼叫性质操作函式时要用到的参数;再来则是抓取命令列的 第二个参数作为性质的名字,指定 False 给 only_if_exists 後,呼叫 XInternAtom 以产生一个新的性质,其转换出来的原子叫做 myproperty。toplevel widget 有两个 孩子及两个孙子,分别是 rc 、 quit_btn 、 data_field 及 put_data,其中 data_field 及 put_data 都是 rc 的孩子。 put_data widget 有一个事件处理程序 (event handler) 叫做 PutData,其主要工作是当 put_data widget 被压下时,呼叫 XmTextGetString 函式将 text widget 中的资料存放到一个缓冲区後,再呼叫 XChangeProperty 函式将缓冲区内的资料存放到根视窗的性质中。 quit_btn widget 有一个回叫程序 (callback procedure) 叫做 delete_prop,其主要工作是呼叫 XDeleteProperty 函式删除该性质的定义,之後呼叫 XtCloseDispaly 函式把应用程式和 X 伺服器的连线 结束掉。 【 get_from_prop.c 】 此程式的主要大纲为:在事件回圈(event loop)中侦收 PropertyNotify 事件,当 PropertyNotify 事件发生时,判断其发生事件的视窗是否为根视窗及性质名字的 原子是否为应用程式所感兴趣的原子;如果是,则呼叫 GetData 程序将性质内的资 料读取出来,显示在 text widget 上;如果其中一个条件不符,则呼叫 XtDispatchEvent 函式将事件丢往它该去的视窗上。程式一开始执行 X 视窗应用程式的初始化工作,以 产生一个 toplevel widget,接著分别呼叫 XtDisplay 及 DefaultRootWindow 以取得应用程式和 X 伺服器的连线及根视窗的视窗识别码,这两个都是呼叫性质操作 函式时会用到的参数;再来则是抓取命令列的第二个参数作为性质的名字,指定 True 给 only_if_exists 後,呼叫 XInternAtom 找出其相对应的原子,如果此性质不存在,则 传回 None。 toplevel widget 有两个孩子及一个孙子,分别是 rc 、 quit_bnt 及 data_field,其中 data_field 为 rc 的孩子。因为性质是悬挂在根视窗下,而根视窗 并不是一个 widget,所以无法使用 XtMainLoop 及 XtAddEventHandler 的函式呼叫来 对 PropertyNotify 事件做出反应,因此应用程式改采 Xlib 的风格来选择事件及设计 事件回圈。GetData 程序中呼叫了 XGetWindowProperty 函式,从根视窗的性质中去读 取资料,其参数 offset 及 length 分别为 0 及 8192,这是一般性质所能存放资料的 最大空间;参数 delete 之值为 False,表示应用程式在读取完性质内的资料後,不去 删除该性质的定义;应用程式所期望的性质资料型态为 XA_STRING ,这是 X 视窗预先 定义(predefined)的性质资料型态的原子,而此函式後面的五个参数皆为结果回传值, 执行完此函式後,应用程式判断其是否执行成功及性质内的真正资料型态是否和应用程式 所期望资料型态的一致,如果两项条件皆符合,接著便呼叫 XmTextSetString 函式将回传回来的 资料显示於 text widget 中,最後呼叫 XFree 函式把 X 视窗系统为应用程式配置的 缓冲空间(ret_property)释放掉。 程式执行画面说明 图一:在背景下分别执行 put_to_prop 及 get_from_prop 两支程式,性质的名字为 「behavior」(put_to_prop behavior&;sleep 5;get_from_prop behavior&), 待视窗出现後在 put_to_prop 的 text widget 中键入资料,此时尚未压下 PutData widget,所以 put_to_prop 的 text widget 中的资料还未存放到 根视窗的 behavior 性质中。 图二:压下在 put_to_prop 中的 PutData widget,此时 put_to_prop 的 text widget 中的资料被存放到悬挂在根视窗的 behavior 性质内,同此时刻 get_from_prop 侦收到 ProertyNotify 事件,接著就到根视窗的 behavior 性质中去读取资料, 并将资料显示於 get_from_prop 的 text widget 中。 /* * Program: put_to_prop.c * * Purpose: Retrieve data from text widget, then put it into * a specified property which is hanged on root window. * * Author : Chung-Chia Chen * * Date : Dec. 14, 1994 */ #include <X11/StringDefs.h> #include <X11/Intrinsic.h> #include <X11/Xatom.h> #include <Xm/Xm.h> #include <Xm/RowColumn.h> #include <Xm/PushB.h> #include <Xm/Text.h> #include <stdio.h> #include <stdlib.h> #define ROWS 10 #define COLS 40 static void PutData(Widget, Widget*, XEvent*); static void DeleteProp(Widget, caddr_t, XmAnyCallbackStruct*); static Display *mydisplay; static Window root_window; static Atom myproperty; void main(int argc, char *argv[]) { Widget toplevel, rc, put_data, data_field, quit_btn; if( argv[1] == NULL) { printf("Usage: program_name property_name\n"); exit(0); } toplevel = XtInitialize(argv[0], "PutDemo", NULL, 0, &argc, argv); mydisplay = XtDisplay(toplevel); if( (root_window = DefaultRootWindow(mydisplay)) == NULL ) { printf("root_window id is null\n"); exit(-1); } /*************************************************** * Get the display and root window id. ***************************************************/ myproperty = XInternAtom(mydisplay, argv[1], False); if( myproperty == None ) { printf("Trying to create argv[1] property failed.", argv[1]); exit(-1); } /********************************************************** * Create a new property, convert the property's name * into an atom called myproperty. * Application takes predefined atom "XA_STRING" as the * data type of the property, so the job that converts * the property's data type into an atom can be exempted. **********************************************************/ rc = XtVaCreateManagedWidget("Panel", xmRowColumnWidgetClass, toplevel, NULL); data_field = XtVaCreateManagedWidget("DataField", xmTextWidgetClass, rc, XmNeditMode, XmMULTI_LINE_EDIT, XmNrows, ROWS, XmNcolumns, COLS, NULL); put_data = XtVaCreateManagedWidget("PutData", xmPushButtonWidgetClass, rc, NULL); XtAddEventHandler(put_data, ButtonPressMask, FALSE , (XtEventHandler) PutData, &data_field); /******************************************************** * Create a push button widget(put_data), then register * an event handler named PutData which solicits button * press event. * data_field is taken as a client data which is gonna * pass to the PutData event handler. ********************************************************/ quit_btn = XtVaCreateManagedWidget("Quit", xmPushButtonWidgetClass, rc, NULL); XtAddCallback(quit_btn, XmNactivateCallback, (XtCallbackProc) DeleteProp, NULL); XtRealizeWidget(toplevel); XtMainLoop(); } static void PutData(Widget w, Widget *client_data, XEvent *ev) { char *buff; buff = XmTextGetString(*client_data); if(buff == NULL) { printf("XmTextGetString returns NULL\n"); return; } XChangeProperty(mydisplay, root_window, myproperty, XA_STRING, 32, PropModeReplace, (unsigned char*) buff, strlen(buff)); /*************************************** * Store the data pointed by buff into * the property named myproperty. ***************************************/ free(buff); } static void DeleteProp(Widget w, caddr_t client_data, XmAnyCallbackStruct *call_data) { XDeleteProperty(mydisplay, root_window, myproperty); XFlush(mydisplay); XtCloseDisplay(mydisplay); exit(0); } /* * Program: get_from_prop.c * * Purpose: Get the data from a property which is hanged on * root window. * * Author : Chung-Chia Chen * * Date : Dec. 12, 1994 */ #include <X11/StringDefs.h> #include <X11/Intrinsic.h> #include <X11/Xatom.h> #include <Xm/Xm.h> #include <Xm/RowColumn.h> #include <Xm/PushB.h> #include <Xm/Text.h> #include <stdio.h> #include <stdlib.h> #define ROWS 10 #define COLS 40 static void GetData(Widget*); static void CloseApp(Widget, XtPointer*, XmAnyCallbackStruct*); static Display *mydisplay; static XEvent myevent; static Window root_window; static Atom myproperty; void main(int argc, char *argv[]) { Widget toplevel, rc, data_field, quit_btn; if( argv[1] == NULL ) { printf("Usage: program_name property_name_that_already_exists\n"); exit(0); } toplevel = XtInitialize(argv[0], "GetDemo", NULL, 0, &argc, argv); mydisplay = XtDisplay(toplevel); if( (root_window = DefaultRootWindow(mydisplay)) == NULL ) { printf("root_window is null, error\n"); exit(0); } /*************************************************** * Get the display and root window id. ***************************************************/ myproperty = XInternAtom(mydisplay, argv[1], True); if( myproperty == None ) { printf("The property named %s does not exist.\n", argv[1]); exit(0); } /************************************************* * Check that the property exists or not. *************************************************/ rc = XtVaCreateManagedWidget("Panel", xmRowColumnWidgetClass, toplevel, NULL); data_field = XtVaCreateManagedWidget("DataField", xmTextWidgetClass, rc, XmNeditMode, XmMULTI_LINE_EDIT, XmNrows, ROWS, XmNcolumns, COLS, NULL); quit_btn = XtVaCreateManagedWidget("Quit", xmPushButtonWidgetClass, rc, NULL); XtAddCallback(quit_btn, XmNactivateCallback, (XtCallbackProc) CloseApp, NULL); XtRealizeWidget(toplevel); XSelectInput(XtDisplay(toplevel), root_window, PropertyChangeMask); /***************************************************** * The root window solicits PropertyChange event. *****************************************************/ while(TRUE) { XtNextEvent(&myevent); switch (myevent.type){ case PropertyNotify: printf("PropertyNotify event occured on root window\n"); if(myevent.xproperty.window = root_window && myevent.xproperty.atom == myproperty) GetData(&data_field); else XtDispatchEvent(&myevent); break; /************************************************ * It's important to check the xproperty.window * and xproperty.atom, otherwise some bad access * event will occur. ************************************************/ default: XtDispatchEvent(&myevent); break; }/* end of switch(myevent.type) */ }/* end of while(TRUE) */ } static void GetData(Widget *data_w) { Atom ret_type; int ret_format; unsigned long ret_len, ret_after; unsigned char *ret_property; if( (XGetWindowProperty(mydisplay, root_window, myproperty, 0, 8192, False, XA_STRING, &ret_type, &ret_format, &ret_len, &ret_after, &ret_property) == Success) && (ret_type == XA_STRING)) { XmTextSetString(*data_w, (char *) ret_property); XFree(ret_property); } else printf("XGetWindowProperty failed\n"); } static void CloseApp(Widget w, XtPointer *client_data, XmAnyCallbackStruct *call_data) { XtCloseDisplay(XtDisplay(w)); exit(0); }