COPYING
มาเขียนโปรแกรมบน X-Window ด้วย Xlib กันเถอะ...
บทนำ
การเขียนโปรแกรมบนระบบ Linux นั้นสามารถแสดงผลได้หลายแบบ เช่นการแสดงผล
เป็น text ออกทาง console เช่น pine, cat หรือ ls เป็นต้น อีกแบบหนึ่งก็คือการแสดงผลเป็น
graphic ซึ่งวิธีนี้ก็มีหลายทางเลือก หนึ่งในทางเลือกที่ใช้กันบ่อยและเป็นมาตรฐานที่สุดคือ
การแสดงผลด้วยการใช้ X-Window ใน X-Window เองก็มีหลายทางเลือกสำหรับprogrammer
เช่นทางเลือกที่จะใช้ tool kit ซึ่งมีอยู่มากมายหลายตัว เช่น Motif หรือGTKหรือการเขียนด้วย
Xlib ซึ่งเป็นการเขียนโปรแกรมในระดับที่ต่ำที่สุด การเขียนโปรแกรมด้วยXlib นี้ แม้ว่าจะไม่
เหมาะกับงานประเภทที่ต้องการความรวดเร็วและง่ายต่อการพัฒนา แต่ก็มีข้อดีคือสามารถ
เขียนโปรแกรมได้อย่างอิสระ และลดความยาวของ code โดยรวมลงได้ ทั้งนี้ก็เพราะว่าใน
tool kit ชนิดต่างๆ โดยพื้นฐานก็ถูกพัฒนาขึ้นด้วยการใช้ Xlib เป็นพื้นฐานกล่าวโดยรวมแล้ว
งานที่ใช้ Xlib เป็นพื้นฐานในการพัฒนาก็ได้แก่ toolkit ชนิดต่างๆ, windowmanager, game,
terminal ต่างๆเป็นต้น แต่ทั้งนี้ก็ไม่ได้หมายความว่า tool kit จะไม่สามารถใช้พัฒนา window
manager, game หรือ terminal อย่างไรก็ตาม การใช้ Xlib ในการพัฒนาโปรแกรมจะทำให้
โปรแกรมทำงานได้เร็วกว่าการใช้ tool kit เนื่องจาก tool kit นั้นมีลำดับชั้นของ header มากมาย
สำหรับเอกสารนี้ จะเป็นการแนะแนวทางคร่าวๆ ในการพัฒนาโปรแกรมด้วย Xlib และอาจ
มีหลายส่วนที่ยังไม่เสร็จสมบูรณ์ หรืออาจมีข้อผิดพลาด แต่ก็คงจะพอมีประโยชน์สำหรับผู้
อ่านไทย ที่ไม่ต้องการจะอ่านคู่มือเป็นภาษาอังกฤษ ดังนั้นหากเกิดความผิดพลาดประการใด
ในเอกสารนี้ก็ช่วยไม่ได้นะ... ก๊ากๆๆ เอ้อขอโทษที... อย่างไรก็ตามผู้เขียนยินดีรับข้อแนะนำ
และคำติชมต่างๆ โดยสามารถส่งมาได้ที่ id@maliwan.org อีกประการหนึ่ง เนื่องจากผู้เขียน
ไม่คุ้นเคยกับ window manager ตัวอื่นๆมากนักนอกเหนือจาก Window Makerดังนั้นตัวอย่างต่างๆ
ที่ผู้เขียน ใช้ในเอกสารนี้จึงถูกทดสอบบน Window Maker เท่านั้น และเนื่องจากผู้เขียนมิได้ศึกษา
การเขียนโปรแกรมด้วย Xlib มาจากเอกสารใดโดยตรง นอกจากการอ่านและพยายามทำความ
เข้าใจเอาจาก source code ของ open source software ต่างๆ ผู้เขียนจึงได้แต่หวังเป็นอย่างยิ่งว่า
ตัวอย่างที่ถูกยกขึ้นในเอกสารนี้คงจะไม่ผิดจากหลักวิชาการไปมากนัก และน่าจะทำงานบน
window manager ที่มีอยู่ชนิดต่างๆมากมายได้เป็นอย่างดี
]d
บทที่ 1 อะไรคือ X Window ?
ความเป็นมา และประวัติอย่างคร่าวๆ
กลไกที่ใช้ในการแสดงผล
การแสดงผลใน X Window เป็นการทำงานแบบ client/serverโดยผ่าน network layer ซึ่งจากการ
ที่เป็นการทำงานผ่าน network นี้ ทำให้ เราสามารถ run program ข้าม hostได้เช่น ตัว execute
image ของ program xterm อาจจะอยู่บน host abc.maliwan.org แต่ว่าตัวX server อาจจะอยู่ที่
linux.thai.net ตัว X server นี้ โดยทั่วไปจะเป็นตัวที่ "คุย" กับ videocard โดยตรง และทำงานอยู่บน
เครื่องที่ใช้แสดงผล หรือมีจอภาพ ดังนั้นในกรณีนี้ user จะต้อง run xtermที่ abc.maliwan.org แต่
ตัว xterm จะสร้าง network connection กับ X server ที่ run อยู่บน linux.thai.net แล้วส่งสิ่งที่จะแสดงผล
กลับมาแสดงยัง X server บน linux.thai.net
รูปที่ 1.1จากรูปที่ 1.1 จะเห็นว่า X server และ process xtermทำงานอยู่คนละเครื่องกัน โดย X server รับผิดชอบ
แสดงผลทั้งหมดบนหน้าจอ และ xterm process จะส่ง message ผ่าน networkมาบอก X server
ว่าจะแสดงผลหน้าต่าง ของ xterm อย่างไรบ้าง เช่น xterm บอกว่าขอให้แสดงอักษร A ที่มุมซ้าย
บนของหน้าต่างของ xterm เอง Xserver ก็จะเขียนอักษร A ลงไปยังตำแหน่งดังกล่าว สำหรับ
user ทั่วๆไปที่ run xterm หรือ application ใดๆบนเครื่องตัวเอง ก็จะหมายถึงการย้าย process xterm
มา run ที่ host เดียวกันกับ X server นั่นเอง ซึ่งจากรูปที่ 1 ได้แยกออกเป็นสอง host เพื่อให้เข้าใจ
หลักการของ X server เท่านั้น และแม้ว่า process xterm กับ X server จะอยู่บน host เดียวกัน การทำ
งานก็ยังต้องผ่าน ระบบ network ภายใน host อยู่ดี แต่อาจจะไม่ใช่ networkprotocal เดียวกับการทำงาน
ระหว่าง host เป็นต้น
window manager คืออะไร
หน้าที่ของ window manager
window manager มีอะไรบ้าง
tool kits
อะไรคือ tool kit
tool kit มีอะไรบ้าง
บทที่ 2 เปิดหน้าต่างบานแรก
window คืออะไร
ในระบบ X window นั้น X server จะมีข้อมูลของ windowหรือหน้าต่างทุกหน้าต่าง โดยแต่ละหน้าต่าง
จะมีค่าตัวเลขค่าหนึ่งซึ่งเป็นค่าประจำตัว โดย window เหล่านี้จะเรียงกันเป็นโครงสร้างโดยเริ่มต้นจาก
root window ซึ่งจะครอบคลุมพื้นที่ทั้งหมดของการแสดงผล และ root windowจะมีwindow ย่อยๆภายใน
ซึ่งใน window ย่อยๆแต่ละ window ก็สามารถมี window ย่อยๆซ้อนอยู่ในตัวเองได้อีก ลองนึกเปรียบเทียบ
กับเรื่อง process ใน unix และเปรียบเทียบ window id กับ process id จะเห็นว่ามีความคล้ายคลึงกัน และ
มีการเรียงลำดับชั้นแบบมี parent กับ child เหมือนๆกัน แต่อย่างไรก็ดีwindow ใน X windowกับ process
ใน unix ไม่ได้ทำงานเกี่ยวข้องกันโดยตรง
รูปที่ 2.1จากรูปที่ 2.1 จะเป็นตัวอย่างการทำงานของ X windowจะเห็นว่า window สามารถซ้อนทับกันได้
หรือ window หนึ่ง อาจจะเป็น window ลูก (subwindow) ของ window อีก windowหนึ่งก็ได้ โดยในรูป
ส่วนที่สีแดงซ้อนกับสีม่วง ได้แสดงเส้นประไว้จำลองหมายความว่า สีม่วงทับสีแดงอยู่ และทั้งคู่ต่างก็
เป็น sub window ของ root window (สีน้ำเงิน) ส่วน window สีฟ้าซึ่งเป็น subwindow ของ window สีเขียว
ได้แสดงให้เห็นว่า subwindow ไม่ถูกจำกัดพื้นที่ด้วย parent windowของมัน โดยได้แสดงเป็นเส้นประ
สีฟ้าไว้ให้เห็นว่า จริงๆแล้ว ตำแหน่ง ของ window สามารถเหลื่อมล้ำออกไปได้แต่อย่างไรก็ตาม ส่วนที่มองเห็นได้จะถูกจำกัดด้วย ขนาดและตำแหน่งของ parent window ของมันทั้งนี้
แต่ละ window จะมีค่า window id ที่แตกต่างกัน โดยทั่วไป rootwindowจะมีค่า 0x25**ลองทดสอบหาค่า window id ของ window ต่างๆด้วยprogram xwininfo โดยทำการเรียก program ชื่อ
xwininfo ใน xterm เมื่อ mouse เปลี่ยนเป็นรูปกากบาท ให้ click บนwindowต่างๆแล้วลองพิจารณาค่าที่
xwininfo พิมพ์ออกมาบน xterm**
ทำความรู้จักกับ Display
สร้าง window อย่างง่ายๆกันก่อน
เริ่ม code กันเล้ย!!example 2.1: myfirsttime.c
#include <X11/Xlib.h>
#include <X11/Xutil.h>int main(int argc, char **argv){
Display* display;
int screen_number;
Window window;
XSetWindowAttributes attributes;
unsigned long attributemask;
Visual *visual;display = XOpenDisplay("");
screen_number = DefaultScreen(display);
visual= CopyFromParent;
attributes.background_pixel=WhitePixel(display, screen_number);
attributes.border_pixel= BlackPixel(display, screen_number);
attributemask = CWBackPixel|CWBorderPixel;
window = XCreateWindow(display, RootWindow(display, screen_number),
200,200,200,200,0,
CopyFromParent,InputOutput,visual,attributemask,&attributes);
XMapRaised(display, window);
XFlush(display);
while(1) {
}
}
การ compile program นี้ ทำได้ด้วยคำสั่ง`cc myfirsttime.c -o myfirsttime -L/usr/X11R6/lib -lX11'
ซึ่งจะได้ executable image ชื่อ myfirsttime ออกมา ซึ่งสั่งให้ทำงานได้ด้วยโดยเรียก `./myfirsttime' ที่
command lineจากตัวอย่างที่ 2-1 ขออธิบายแต่ละบรรทัดตามลำดับดังนี้
#include <X11/Xlib.h>
include header file Xlib
int main(int argc, char **argv){
ประกาศค่าตัวแปรที่จำเป็นจะต้องใช้
Display* display;
ตัวแปร display เป็น pointer ไปยัง structure Display, Display นี้จะเป็นตัวที่ใช้บอกว่าเราจะแสดงผลโปรแกรม
ของเราบน X server ตัวไหน การสื่อสารระหว่างโปรแกรมกับ X server จะต้องผ่านค่า Display นี้ไปด้วยเสมอ
โดยทั่วไปแล้ว โปรแกรมหนึ่งๆจะมีการสร้าง Displayนี้เพียงตัวเดียวเท่านั้น กล่าวคือ โปรแกรมใดๆมักจะ
มีหน้าต่างแสดงผลแค่ที่บนหน้าจอเดียวเท่านั้น (อย่างไรก็ตามหน้าจอหนึ่งอาจจะสามารถมีได้หลาย
Display สามารถสลับกันแสดงผลได้ เช่น host หนึ่งกับจอภาพหนึ่งสามารถมี X server ทำงานอยู่ได้หลายตัว
แต่เพื่อไม่ให้เกิดความสับสน ผู้อ่านยังไม่จำเป็นจะต้องทำความเข้าใจเรื่องดังกล่าวในตอนนี้)int screen_number;
ใน X server หนึ่งก็ยังอาจมีหลายหน้าจอซ้อนกันอยู่ โดยใช้ screen number เป็นตัวแยก หน้า screen
แต่ละ screen ออกจากกัน แต่ละ screen ก็จะมี mousepointer และ keyboard input เป็นอิสระจาก screen
อื่นๆ อย่างไรก็ตามผู้เขียนจะไม่ขอพูดถึงการใช้screen ในระดับนี้Window window;
window หรือหน้าต่าง ซึ่งก็คือพื้นที่หนึ่งใน X server มีลักษณะเฉพาะตัวของตัวเอง เช่น ตำแหน่งที่อยู่
และแต่ละ window จะมี id เป็นของตัวเองไม่ซ้ำกับwindow อื่น อ้างอิงถึงได้ด้วย Window (ซึ่งก็คือค่าของ
window id)XSetWindowAttributes attributes;
unsigned long attributemask;ในแต่ละ window จะมีสมบัติเฉพาะตัวของตัวเองหลายอย่าง ค่าบางค่าในค่าเหล่านั้นก็คือค่า window
attribute ซึ่งการจะขอให้ X server แก้ไขค่า attribute ของ window นั้น application จะต้องอ้างอิงผ่าน
structure XSetWindowAttributes ซึ่งมีค่าหลายค่าอยู่ใน structure และต้องการตัวแปรอีกตัวหนึ่งคือ
attribute mask ไว้คอยบอก X server ว่าต้องการset ค่าใดบ้างใน structureVisual *visual;
Visual ก็เป็นค่าๆหนึ่งที่จะบอกว่า window นั้นๆมีระบบสีอย่างไร เรื่องสีใน X window เป็นเรื่องที่ซับซ้อน
มากที่สุดเรื่องหนึ่ง แต่ในระดับนี้จะยังไม่ขอกล่าวถึงรายละเอียดในเรื่องสีซึ่งผู้เขียนคิดว่าไม่จำเป็นมาก
เท่าไหร่display = XOpenDisplay("");
จาก code เป็นการสร้าง connection กับ X server โดยการผ่าน string เปล่าให้ หมายถึงเป็นการขอติดต่อ
กับ default X server ซึ่งโดยทั่วไปก็มักจะเป็นX server ตัวหนึ่งที่กำลังทำงานอยู่ในเครื่อง
จาก ตัวอย่าง function XOpenDisplay จะ returnค่าของ Display ออกมาให้กับ pointer display ซึ่งค่า display
นี้ เราจะต้องใช้ต่อไปโดยเฉพาะทุกครั้งที่มีการติดต่อกับ X serverscreen_number = DefaultScreen(display);
จากค่า display ที่หาได้ ค่าต่อไปที่เราต้องใช้คือค่า screen ซึ่งเราสามารถหาออกมาได้ตามตัวอย่าง
DefaultScreen ก็คือ default screen ของ displayนั้นๆ โดยทั่วไปก็คือภาพที่เรามองอยู่นั่นเองvisual= CopyFromParent;
กำหนดค่าของ Visual ให้เป็นCopyFromParent ซึ่งหมายความว่า ให้ Window ที่ต้องการจะสร้าง ใช้
ค่า Visual เดียวกับ parent ของ Window นั้น ซึ่งทุกๆ Window จะมี parent window เป็นของตัวเองยกเว้น
root window ซึ่งเป็น Window ที่อยู่ชั้นบนสุดของลำดับattributes.background_pixel= WhitePixel(display, screen_number);
attributes.border_pixel= BlackPixel(display, screen_number);
attributemask = CWBackPixel|CWBorderPixel;ตั้งค่า attribute ที่จะใช้ในการสร้างWindow โดยจากตัวอย่างแสดงถึงการตั้งค่าเพียง 2 ค่าเท่านั้น
ได้แก่ค่าสีพื้นและค่าสีขอบของ Window Window สามารถมีขอบได้ ขอบนี้จะเป็นเส้นตรงซึ่งเรา
สามารถตั้งความกว้างให้มีค่า 0 คือไม่มีความกว้างเลยก็ได้ ตามตัวอย่างต่อไป ส่วนค่าที่เราผ่าน
ให้ attributemask นั้นก็หมายความว่าเราต้องการใช้ค่าใน attributes เพียงสองค่าคือค่า BlackPixel
กับ BorderPixel หรือสมาชิก background_pixel และborder_pixel ตามลำดับ โดยการตั้งค่า mask
นี้ก็ทำได้โดยเอาค่า mask ที่ต้องการมา or กันในระดับ bit"จริงๆแล้วผู้เขียนไม่เคย set สีของ border ได้เลย เพราะยังไม่สามารถจะทำให้มองเห็น border
ได้ โดยได้พยายามตั้งเป็นค่าต่างๆในตอน create window แต่ก็ไม่สำเร็จ"window = XCreateWindow(display, RootWindow(display, screen_number),
200,200,200,200,0,
CopyFromParent,InputOutput,visual,attributemask,&attributes);สร้าง Window แรก ด้วย function XCreateWindow โดยผ่านค่าต่างๆมากมายให้กับ function
โดยแต่ละค่ามีความหมายตามลำดับดังต่อไปนี้
ค่าแรก (Display *)display ก็คือค่าที่เราหาได้จาก XOpenDisplay ซึ่งต้องอ้างถึงเสมอ เมื่อจะติดต่อกับ
X server
ค่าที่สอง (Window)parent window ในที่นี้คือ RootWindow(display, screen_number) เป็น macro(?)
ที่ใช้อ้างถึง root window ของ display และ screenและที่เราผ่านค่า root window ให้ XCreateWindow นี้ เพื่อเป็นการระบุว่าเราต้องการจะสร้าง Window ซึ่งเป็น Window ที่เป็นลูกของ root window ซึ่งที่จริงแล้วWindow ที่จะสร้างขึ้นนี้จะเป็นลูกของ Window ใดก็ได้ที่มีอยู่บน X serverในที่นี้ เราใช้ root window ก็หมายความว่าเราต้องการให้ window ของเราเป็นsubwindow ของ root window
ค่าที่สาม สี่ ห้า และ หก เป็นตัวเลขที่แสดงถึง geometry ของ Window คือ ตำแหน่งบน parent window
เป็นค่า X,Y, ความกว้าง และยาวตามลำดับ
ค่าที่เจ็ด border_width เป็นค่าความกว้างของขอบของ window
ค่าที่แปด depth ค่าๆนี้เกี่ยวข้องกับสี ซึ่งสลับซับซ้อน และจะไม่ยังไม่กล่าวถึงในตอนนี้ ตัวแปรนี้จึง
ให้เป็น CopyFromParent เช่นเดียวกับ visual คือให้ใช้ค่าเดียวกันกับ parent window นั่นเอง
ค่าที่เก้า class จะยังไม่กล่าวถึงในตอนนี้ แต่ขอให้ InputOutput ไป
ค่าที่สิบ สิบเอ็ด และสิบสอง คือ visual, attribute mask และ attributes ซึ่งได้กล่าวไปแล้วการสร้าง window นี้ดูจะวุ่นวายมาก แต่เราอาจสามารถย่นย่อการสร้าง window ได้ด้วย function
XCreateSimpleWindow เช่นwindow = XCreateSimpleWindow(display, RootWindow(display, screen_number),
200,300,200,300,10,WhitePixel(display, screen_number),
BlackPixel(display, screen_number));ในกรณีนี้เราก็ไม่จำเป็นต้องมีการควบคุมค่าของ window attributes โดยจากตัวอย่าง function นี้
จาก argument ตัวที่ 1 ถึง 7 จะยังคงเหมือนกับfunction XCreateWindow แต่ส่วนที่เหลือจะต้องการ
argument เพิ่มอีกเพียง 2 ตัว คือสีของขอบและสีของพื้นหลังตามลำดับ ซึ่งถ้าใช้ XCreateWindow
จะต้องผ่านค่าดังกล่าวผ่านโครงสร้าง XSetWindowAttributes ซึ่งซับซ้อนกว่าแต่ว่าก็มีความจำเป็นที่จะ
ต้องใช้ในหลายๆกรณีเช่นกันXMapRaised(display, window);
การ create window นั้นเป็นการขอให้ X server สร้าง window ขึ้น เท่านั้น แต่ไม่ได้ขอให้นำ window
ที่สร้างขึ้นแล้วมาแสดงผล XMapRaised จะเป็นการนำwindow ที่ถูกสร้างขึ้นนั้นมาแสดงผลให้
เห็นได้ด้วยตา
อย่างไรก็ตาม การนำ windowแต่ละ window ขึ้นแสดงผลนั้น หากมี window manager ทำงานอยู่ด้วย
จะต้องผ่านการเห็นชอบของ window manager ก่อน และการที่ window จะถูกวางไว้ที่ตำแหน่งไหนนั้น
ก็เป็นหน้าที่ของ window manager ที่จะต้องตัดสินใจ และค่าต่างๆที่ผ่านให้ window manager นั้นก็จะ
เป็นเพียงแค่ค่าที่ใช้แนะนำหรือขอร้อง window manager เท่านั้นXFlush(display);
หากไม่มีการ flush แล้ว request ของ application จะไม่ได้รับการประมวลผลจาก X server
และ window อาจไม่ถูก map ขึ้นบนหน้าจอ
ผู้เขียนเข้าใจว่าอาจเป็นเพราะ request ของapplication ไปค้างอยู่ใน network buffer ใน
network layer (?)while(1) {
}endless loop ตรงนี้มีไว้เพื่อให้โปรแกรมทำงานแบบไม่รู้จบเพื่อค้าง window ที่ถูกสร้างขึ้นเอาไว้เท่านั้นหากผู้อ่านต้องการให้โปรแกรมเลิกการทำงาน ก็สามารถทำได้ด้วย
การกด Control-C จาก terminal ที่ใช้ใช้เรียกคำสั่งดังกล่าว แต่โดยทั่วไปแล้ว loop ตรงนี้จะมีไว้เพื่อคอยวนรออ่านค่าที่ส่งกลับมาจากX server เช่นค่า keyboard
หรือ mouse input ซึ่งเรียกว่าค่า Event ซึ่งจะได้กล่าวต่อไป}
ทาสีหน้าต่างเล่น
เมื่อเราได้สร้างหน้าต่างเปล่าๆขึ้นมาได้แล้ว ขั้นต่อไปก็คือการแสดงผลบนหน้าต่างที่ได้สร้างขึ้นมา
ตัวอย่างที่จะนำมาแสดงในขั้นตอนนี้ คือการวาดวงกลมและสี่เหลี่ยมลงบนหน้าต่าง ดังต่อไปนี้example 2.2: paintit.c
#include <X11/Xlib.h>จากตัวอย่างที่ 2.2 ได้แสดงส่วนที่เพิ่มเติมขึ้นมาจากตัวอย่างแรก ส่วนที่เพิ่มขึ้นมานี้แบ่งออกเป็น
#include <X11/Xutil.h>int main(int argc, char **argv){
Display* display;
int screen_number;
Window window;
XSetWindowAttributes attributes;
unsigned long attributemask;
Visual *visual;GC gc;
XGCValues gcv;display = XOpenDisplay("");
screen_number = DefaultScreen(display);
visual= CopyFromParent;
attributes.background_pixel= WhitePixel(display, screen_number);
attributes.border_pixel = WhitePixel(display, screen_number);
attributemask = CWBackPixel|CWBorderPixel;
window = XCreateWindow(display, RootWindow(display, screen_number),
200,200,200,200,0,
CopyFromParent,InputOutput,visual,attributemask,&attributes);
XMapRaised(display, window);
XFlush(display);gcv.foreground = 12345;
gc=XCreateGC(display, window, GCForeground, &gcv);while(1) {
static int i=0;
XDrawArc(display, window,gc, 10+i++, 10+i++, 150, 150, 0, 10000);
sleep(3);
XFlush(display);
sleep(3);
}
}
ส่วนของการสร้าง GC หรือ graphics context ซึ่งสามารถเปรียบเทียบได้กับปากกา ที่เราสามารถ
ปรับเปลี่ยนสีหมึก ความกว้างเส้น วิธีการวาดได้ตามความต้องการ ซึ่งผู้เขียนจะได้กล่าวในโอกาส
ต่อไปในบทของการจัดการกับรูปภาพ และการแสดงผลอักขระ แต่ในตอนนี้ จะขออธิบายเพียง
คร่าวๆเท่านั้นGC gc;
XGCValues gcv;ในตอนแรก เป็นการประกาศค่าตัวแปรที่จำเป็นในการใช้ GC ได้แก่ตัวแปรชนิด GC (gc) ซึ่งก็คือตัว
graphics context เอง และตัวแปรชนิด XGCValues (gcv) ซึ่งมีไว้ใช้ในการตั้งค่าคุณสมบัติของ GCgcv.foreground = 12345;
gc=XCreateGC(display,window, GCForeground, &gcv);จากนั้นก็สร้าง GC ขึ้น โดยจากตัวอย่างจะเป็นการเลือกสีที่จะใช้วาด หรือสีพื้นหน้า (foreground)
ด้วยการให้ค่า gcv.foreground ด้วยค่า 12345 ซึ่งค่าๆนี้จะหมายถึงค่าของสีแต่อย่างไรก็ดี ค่าสี
นี้จะมีความหมายได้หลายอย่าง กล่าวคือ ถ้าหากโปรแกรมแสดงผลบนระดับการแสดงผล256 สี
สีดังกล่าวอาจจะเป็นสีอะไรก็ได้ ซึ่งทั้งนี้ก็ขึ้นอยู่กับ Colormap แต่ถ้าโปรแกรมทำงานอยู่บนระดับ
การแสดงผลแบบ true color ก็จะพบว่าสีดังกล่าวเป็นสีน้ำเงิน ค่าของgcv.foreground นี้เป็นค่า
unsigned long integerให้ผู้อ่านทดลองเปลี่ยนค่าตัวเลขตัวนี้เป็นค่าต่างๆ เพื่อดูความเปลี่ยนแปลงของผลที่เกิดขึ้น
ขั้นตอนต่อจากการตั้งค่า gcv ก็เป็นการสร้าง gc ขึ้น โดยผ่าน structure gcv ให้กับ function XCreateGC
ด้วย ซึ่งตอนนี้ เราก็จะได้ gc ซึ่งมีค่าสีพื้นหน้าเป็นสี 12345static int i=0;
XDrawArc(display, window,gc, 10+i++, 10+i++, 150, 150, 0,10000);
sleep(3);
XFlush(display);
sleep(3);ต่อจากนั้นเราก็ทำการวาดส่วนโค้งด้วย function XDrawArc โดยจากตัวอย่าง หมายความว่าให้วาด
ส่วนโค้งผ่าน display ลงบน window ด้วย gc โดยหากมีการวาดส่วนโค้งนี้จนเต็มวง ส่วนโค้งจะถูก
จำกัดขนาดพอดีกับกรอบสี่เหลี่ยมกรอบหนึ่ง ที่มีจุดมุมซ้ายบนอยู่ที่ตำแหน่งxและ y เท่ากับ 10
และสี่เหลี่ยมมีความกว้างและยาวเท่ากันคือ 150 pixel การวาดส่วนโค้งนี้ให้เริ่มตั้งแต่ มุม 0 องศา
ไปจนถึงมุม 10000/64 องศา (หรือประมาณ 156 องศา) และเนื่องจากคำสั่งนี้จะอยู่ใน while(1) loop
ซึ่งจะทำงานต่อไปเรื่อยๆ ไม่สิ้นสุด ผลที่ได้ก็คือมันจะวาดส่วนโค้งที่ตำแหน่งเดิมๆซ้ำๆกันไป
เรื่อยๆ โดยจะวาดทุก 6 วินาที โดยจะแสดงผลภายใน 3 วินาทีหลังจากวาดลงไปในแต่ละครั้ง
และ loop จะวาดส่วนโค้งหลายภาพ โดยให้ตำแหน่งคลาดเคลื่อนไปทีละนิด ด้วยการเพิ่มค่าตัว
แปร i จากตรงนี้ ให้ผู้อ่านลองลาก window อื่นเข้ามาทับ (อาจจำเป็นจะต้องปล่อย mouse ก่อน)
แล้วลาก window นั้นออกไปที่อื่น จะเห็นว่าส่วนที่ถูกลาก window เข้ามาทับนั้น จะหายไป แต่การ
วาดก็ยังคงมีต่อไปเรื่อยๆ ซึ่งผู้อ่านคงจะรู้แล้วว่า window จะไม่เก็บรูปส่วนที่เขียนลงไปเอาไว้
ใน buffer โดยอัตโนมัติ ดังนั้น เราจึงต้องวาดซ้ำด้วยตัวเอง แต่ถ้าจะใช้วิธี sleep แล้ววาดทับลง
ไปเป็นระยะดังเช่นในตัวอย่างที่ยกมานี้คงจะเป็นวิธีที่ไม่ดีนัก และเหมาะกับการใช้งานบางอย่าง
ที่ window มีการเปลี่ยนแปลงตลอดเวลา เช่น game เท่านั้น วิธีการที่เหมาะสมคือการ handle
Expose Eventตรงนี้ให้ผู้อ่านลองเล่นกับfunction XFillArc ก่อน โดยจากตัวอย่างเดิม ผู้เขียนเพียงแต่เปลี่ยน
function XDrawArc เป็น XFillArc และ/หรือทำการเปลี่ยนแปลงค่าต่างๆที่ผ่านให้กับ function เช่น
ค่ามุมเริ่มต้น มุมสิ้นสุด ขนาดของส่วนโค้ง เพื่อวาดวงกลม วงรี หรือส่วนโค้งในแบบต่างๆกัน
ตามรูปแบบของ function ดังต่อไปนี้XFillArc(display, window, gc, มุมซ้ายบน x, มุมซ้ายบน y,
ความกว้างของส่วนโค้งเต็มตามแนนนอน, แนวตั้ง,
มุมเริ่มต้นx 64, มุมสิ้นสุด x 64)
รูปที่ 2.2จากรูปที่ 2.2 เป็นผลจากการ run program วาดส่วนโค้งตามตัวอย่างที่ 2.2ตำแหน่ง และขนาดของ
ส่วนโค้งจะสัมพันธ์กับค่าต่างๆที่ผ่านให้กับ function XDrawArc และส่วนโค้งที่วาดจะเป็นส่วนโค้ง
ของวงกลม แทนที่จะเป็นวงรี ก็ต่อเมื่อขนาดของความกว้างเท่ากันกับขนาดความยาว ส่วนที่เป็น
สีแดงเป็นส่วนที่ผู้เขียนวาดเพิ่มให้ดูว่ามุมซ้ายบน และความกว้าง หมายถึงอะไร ดังนั้นส่วนที่ผู้อ่าน
จะเห็นในโปรแกรมจะเป็นส่วนสีน้ำเงินเท่านั้น จะเห็นว่าขนาดความกว้างจะหมายถึงความกว้าง
ของวงเต็มวง มิใช่เพียงขนาดของส่วนโค้งเท่านั้น"เมื่อผู้อ่านลองลากหน้าต่างอื่นๆมาปล่อยทับ หน้าต่างของโปรแกรมนี้ แล้วลากออกไปเพื่อดูผลที่เกิด
ขึ้นว่าเส้นที่ได้วาดเอาไว้หายไปจริงหรือไม่ ซึ่งในบางครั้ง ผู้อ่านอาจจะพบว่าเส้นที่ถูกวาดไว้อาจไม่
หายไปก็ได้ นั่นอาจเป็นเพราะว่า window manager ได้ทำการ set "save under" flag ของ window
ที่ถูกลากมาทับก็ได้ อย่างไรก็ตาม ผู้อ่านยังไม่จำเป็นต้องทำความเข้าใจเรื่องดังกล่าวในตอนนี้
และผู้เขียนจะหาโอกาสอธิบายรายละเอียดในโอกาสต่อไป"ตอนนี้เราก็รู้วิธีการวาดวงกลม วงรี และส่วนโค้งไปแล้วเรื่องต่อไปที่เราจะพูดถึงคือเรื่อง Event
Event คืออะไร Event คือสัญญาณที่ process จะได้รับ จาก X server โดยผ่านทาง DISPLAY ซึ่ง Event นี้คือกลไก
หลักของการทำงาน Event มีหลายหน้าที่ และเกิดขึ้นได้เพราะหลายกรณี เช่นการกด keyboard การขยับ
หรือกด mouse การที่ window ถูกเปลี่ยนขนาดหรือมีบางส่วนถูกเปิดเผยขึ้น เช่นถ้าเราลาก window A มาทับ
window B แล้วลาก window A ออกไป จะทำให้เกิด Expose Event ขึ้นบน window Bทำให้เราทราบว่า เราควรจะ
redraw window B ใหม่แล้ว เราลองมาดู code ตัวอย่างดังต่อไปนี้example 2.3: event.c
#include <X11/Xlib.h>
#include <X11/Xutil.h>int main(int argc, char **argv) {
Display* display;
int screen_number;
Window window;
XSetWindowAttributes attributes;
unsigned long attributemask;
Visual *visual;GC gc;
XGCValues gcv;
XEvent event;display = XOpenDisplay("");
screen_number = DefaultScreen(display);
visual= CopyFromParent;
attributes.background_pixel= WhitePixel(display, screen_number);
attributes.border_pixel = WhitePixel(display, screen_number);
attributes.event_mask = KeyPressMask|ExposureMask|ButtonPressMask|StructureNotifyMask;
attributemask = CWBackPixel|CWBorderPixel|CWEventMask;
window = XCreateWindow(display, RootWindow(display, screen_number),
200,200,200,200,0,
CopyFromParent,InputOutput,visual,attributemask,&attributes);
XMapRaised(display, window);
XFlush(display);gcv.foreground = 12345;
gc=XCreateGC(display, window, GCForeground, &gcv);while (1) {
XNextEvent(display,&event);
printf("An event %d arrived: ", event.type);
switch (event.type) {
case Expose:
XFillArc(display, window, gc, 10, 10, 150, 150, 0, 10000);
printf ("We got an expose event, redraw!\n");
break;case KeyPress:
printf ("We got a key event and the keycode is %d\n", event.xkey.keycode);
break;case ButtonPress:
printf ("We got a mouse click at (%d,%d)\n", event.xbutton.x, event.xbutton.y);
break;case ConfigureNotify:
printf ("Structure notify\n");
break;default:
printf ("Something else??\n");
}
}
}
จากตัวอย่างข้างต้นจะเป็นเพียงการแสดงการทำงานอย่างคร่าวๆของการ handle Event เท่านั้น และใบบทนี้เราจะไม่ลง
ลึกไปมากกว่านี้ถึงในรายละเอียดของ Event แต่ละชนิด แต่จะไปกล่าวถึงในบทต่อๆไปXNextEvent(display,&event);
XNextEvent เป็น function ที่จะอ่าน event ต่างๆที่ X server ส่งมา โดย event นี้จะอยู่ในคิว คำสั่ง XNextEvent จะดึง
event ออกมาทีละตัว และถ้าไม่มี event อยู่ในคิว function จะ
เราสามารถprintf("An event %d arrived: ", event.type);
switch (event.type) {
case Expose:
XFillArc(display, window, gc, 10, 10, 150, 150, 0, 10000);
printf ("We got an expose event, redraw!\n");
break;case KeyPress:
printf ("We got a key event and the keycode is %d\n", event.xkey.keycode);
break;case ButtonPress:
printf ("We got a mouse click at (%d,%d)\n", event.xbutton.x, event.xbutton.y);
break;case ConfigureNotify:
printf ("Structure notify\n");
break;default:
printf ("Something else??\n");
การแสดงผลอักขระ
- ทำอย่างไรดีเมื่อเกิด expose event
- key pressed event
- button pressed event
- motion notify event
- event อื่นๆ
บทที่ 3 อะตอม
บทที่ 4 property
บทที่ 5 selection
บทที่ 6 สี
บทที่ 7 จัดการกับรูปภาพ และการแสดงผลอักขระ
บทที่ 8 เจาะลึก event
บทที่ 9 เจาะลึก tool kit
บทที่ 10 เจาะลึก (อีกแล้ว) window manager
บทที่ 11 library ที่น่าสนใจ
พอก่อน