分类存档: Linux C Program

Opencv的opencv_haartraining和opencv_traincascade训练

网上一大堆Opencv的opencv_haartraining和opencv_traincascade训练教程,我试着用了一下,别人的经验多少有些问题,我把我的使用经验和大家分享一下。我的环境如下:

操作系统:Archlinux 4.1.3-1,Opencv版本:2.4.11-1

1.训练先要准备正样本和负样本。

正样本就是要识别的目标图像了,我是手动一个一个的抠出来的,长宽大致相同就可以了,我把正样本存在/home/foo/pos目录下,一共140个文件。负样本就是不含目标的图像,我的负样本保存在/home/foo/neg目录下,一共900个。网上有人说图像文件名称最好是普通的名称复杂的汉字或者符号有可能opencv识别不了,我乖乖的起了很简单的文件名。此外还要准备正、负样本的清单。

/home/foo/pos/posdata.txt文件是正样本文件清单,手动建立该文件,格式如下

1.jpg 1 0 0 200 199

2.jpg 1 0 0 180 190

其中1.jpg是正样本文件名,1是图像中目标个数,0,0 是目标在图像中的左上角,200,199是目标在图像中的右下角。手动添加很累,我是做了个小程序自动生成的。

/home/foo/neg/negdata.txt文件是负样本文件清单,手动建立该文件,格式如下

1.jpg

2.jpg

其中1.jpg是负样本文件名

2.生成vec文件。

准备好样本图像需要使用opencv_createsamples生成.vec文件,就是将这个样本图像进行大小之类的调整,加速训练过程。vec文件生成命令如下:

opencv_createsamples -info pos/posdata.txt  -bg neg/negdata.txt -num 140 -w 24 -h 24 -vec myvec_24.vec

在/home/foo目录下运行该命令,运行后/home/foo目录下会出现myvec_24.vec文件。

-info pos/posdata.txt是指定正样本清单为pos/posdata.txt

-bg neg/negdata.txt是指定负样本清单为neg/negdata.txt

-num 140是指定正样本图像个数为140个

-w 24 -h 24是指将正样本尺寸转换为宽24x高24大小,如果样本过大那么训练过程会很慢的

-vec myvec_24.vec指定vec文件名称为myvec_24.vec

3.使用opencv_traincascade训练

在/home/foo目录下使用命令

opencv_traincascade -data tmp -vec myvec_24.vec -bg neg/negdata.txt -numPos 140 -numNeg 900 -numStages 20 -w 24 -h 24 -featureType HOG

进行训练。其中

-data tmp是指定训练结果存放的目录为tmp,tmp目录要事先建立。

-vec myvec_24.vec是指定vec文件为myvec_24.vec

-bg neg/negdata.txt是指定负样本清单为neg/negdata.txt

-numPos 140是指定正样本数目为140

-numNeg 900是指定负样本数目为900

-numStages 20指定训练阶数为20,太大会很慢

-w 24 -h 24 样本的尺寸,应和生成vec文件时的参数-w -h一致

-featureType HOG使用HOG特征,其他几个参数我实验了,不能用,报错,只有这个能用

然后就是等待了,会出现HR击中率,FA虚警率等信息

训练完成后会有一个xml文件,保存训练结果,不过我虽然训练成功了,但这个文件我用程序加载不上,加载报错。

3.使用opencv_haartraining训练

由于使用opencv_traincascade训练的结果无法加载,我没兴趣查找这个原因了,于是我用老的opencv_haartraining进行训练,训练的结果可以加载使用。

在/home/foo/下运行

opencv_haartraining -data tmp -vec myvec_24.vec -w 24 -h 24 -bg neg/negdata.txt  -nstages 20 -npos 140 -nneg 900

其中

-data tmp是指定中间结果存放的目录为tmp,该目录要提前建立

-nstages 20指定训练阶数为20,太大会很慢

-npos 140 指定正样本数目为140

-nneg 900指定负样本数目为900

漫长等待后,/home/foo目录下会出现一个xml文件,用这个文件就可以进行识别了。

 

 

 

转载jpeglib使用实例

[cc lang="c"]

/*
 * example.c
 *
 * This file illustrates how to use the IJG code as a subroutine library
 * to read or write JPEG image files.  You should look at this code in
 * conjunction with the documentation file libjpeg.doc.
 *
 * This code will not do anything useful as-is, but it may be helpful as a
 * skeleton for constructing routines that call the JPEG library.  
 *
 * We present these routines in the same coding style used in the JPEG code
 * (ANSI function definitions, etc); but you are of course free to code your
 * routines in a different style if you prefer.
 */

#include <stdio.h>

/*
 * Include file for users of JPEG library.
 * You will need to have included system headers that define at least
 * the typedefs FILE and size_t before you can include jpeglib.h.
 * (stdio.h is sufficient on ANSI-conforming systems.)
 * You may also wish to include "jerror.h".
 */

#include "jpeglib.h"

/*
 * <setjmp.h> is used for the optional error recovery mechanism shown in
 * the second part of the example.
 */

#include <setjmp.h>



/******************** JPEG COMPRESSION SAMPLE INTERFACE *******************/

/* This half of the example shows how to feed data into the JPEG compressor.
 * We present a minimal version that does not worry about refinements such
 * as error recovery (the JPEG code will just exit() if it gets an error).
 */


/*
 * IMAGE DATA FORMATS:
 *
 * The standard input image format is a rectangular array of pixels, with
 * each pixel having the same number of "component" values (color channels).
 * Each pixel row is an array of JSAMPLEs (which typically are unsigned chars).
 * If you are working with color data, then the color values for each pixel
 * must be adjacent in the row; for example, R,G,B,R,G,B,R,G,B,... for 24-bit
 * RGB color.
 *
 * For this example, we'll assume that this data structure matches the way
 * our application has stored the image in memory, so we can just pass a
 * pointer to our image buffer.  In particular, let's say that the image is
 * RGB color and is described by:
 */

extern JSAMPLE * image_buffer;	/* Points to large array of R,G,B-order data */
extern int image_height;	/* Number of rows in image */
extern int image_width;		/* Number of columns in image */


/*
 * Sample routine for JPEG compression.  We assume that the target file name
 * and a compression quality factor are passed in.
 */

GLOBAL(void)
write_JPEG_file (char * filename, int quality)
{
  /* This struct contains the JPEG compression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   * It is possible to have several such structures, representing multiple
   * compression/decompression processes, in existence at once.  We refer
   * to any one struct (and its associated working data) as a "JPEG object".
   */
  struct jpeg_compress_struct cinfo;
  /* This struct represents a JPEG error handler.  It is declared separately
   * because applications often want to supply a specialized error handler
   * (see the second half of this file for an example).  But here we just
   * take the easy way out and use the standard error handler, which will
   * print a message on stderr and call exit() if compression fails.
   * Note that this struct must live as long as the main JPEG parameter
   * struct, to avoid dangling-pointer problems.
   */
  struct jpeg_error_mgr jerr;
  /* More stuff */
  FILE * outfile;		/* target file */
  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
  int row_stride;		/* physical row width in image buffer */

  /* Step 1: allocate and initialize JPEG compression object */

  /* We have to set up the error handler first, in case the initialization
   * step fails.  (Unlikely, but it could happen if you are out of memory.)
   * This routine fills in the contents of struct jerr, and returns jerr's
   * address which we place into the link field in cinfo.
   */
  cinfo.err = jpeg_std_error(&jerr);
  /* Now we can initialize the JPEG compression object. */
  jpeg_create_compress(&cinfo);

  /* Step 2: specify data destination (eg, a file) */
  /* Note: steps 2 and 3 can be done in either order. */

  /* Here we use the library-supplied code to send compressed data to a
   * stdio stream.  You can also write your own code to do something else.
   * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
   * requires it in order to write binary files.
   */
  if ((outfile = fopen(filename, "wb")) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    exit(1);
  }
  jpeg_stdio_dest(&cinfo, outfile);

  /* Step 3: set parameters for compression */

  /* First we supply a description of the input image.
   * Four fields of the cinfo struct must be filled in:
   */
  cinfo.image_width = image_width; 	/* image width and height, in pixels */
  cinfo.image_height = image_height;
  cinfo.input_components = 3;		/* # of color components per pixel */
  cinfo.in_color_space = JCS_RGB; 	/* colorspace of input image */
  /* Now use the library's routine to set default compression parameters.
   * (You must set at least cinfo.in_color_space before calling this,
   * since the defaults depend on the source color space.)
   */
  jpeg_set_defaults(&cinfo);
  /* Now you can set any non-default parameters you wish to.
   * Here we just illustrate the use of quality (quantization table) scaling:
   */
  jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);

  /* Step 4: Start compressor */

  /* TRUE ensures that we will write a complete interchange-JPEG file.
   * Pass TRUE unless you are very sure of what you're doing.
   */
  jpeg_start_compress(&cinfo, TRUE);

  /* Step 5: while (scan lines remain to be written) */
  /*           jpeg_write_scanlines(...); */

  /* Here we use the library's state variable cinfo.next_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   * To keep things simple, we pass one scanline per call; you can pass
   * more if you wish, though.
   */
  row_stride = image_width * 3;	/* JSAMPLEs per row in image_buffer */

  while (cinfo.next_scanline < cinfo.image_height) {
    /* jpeg_write_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could pass
     * more than one scanline at a time if that's more convenient.
     */
    row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
    (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
  }

  /* Step 6: Finish compression */

  jpeg_finish_compress(&cinfo);
  /* After finish_compress, we can close the output file. */
  fclose(outfile);

  /* Step 7: release JPEG compression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_compress(&cinfo);

  /* And we're done! */
}


/*
 * SOME FINE POINTS:
 *
 * In the above loop, we ignored the return value of jpeg_write_scanlines,
 * which is the number of scanlines actually written.  We could get away
 * with this because we were only relying on the value of cinfo.next_scanline,
 * which will be incremented correctly.  If you maintain additional loop
 * variables then you should be careful to increment them properly.
 * Actually, for output to a stdio stream you needn't worry, because
 * then jpeg_write_scanlines will write all the lines passed (or else exit
 * with a fatal error).  Partial writes can only occur if you use a data
 * destination module that can demand suspension of the compressor.
 * (If you don't know what that's for, you don't need it.)
 *
 * If the compressor requires full-image buffers (for entropy-coding
 * optimization or a multi-scan JPEG file), it will create temporary
 * files for anything that doesn't fit within the maximum-memory setting.
 * (Note that temp files are NOT needed if you use the default parameters.)
 * On some systems you may need to set up a signal handler to ensure that
 * temporary files are deleted if the program is interrupted.  See libjpeg.doc.
 *
 * Scanlines MUST be supplied in top-to-bottom order if you want your JPEG
 * files to be compatible with everyone else's.  If you cannot readily read
 * your data in that order, you'll need an intermediate array to hold the
 * image.  See rdtarga.c or rdbmp.c for examples of handling bottom-to-top
 * source data using the JPEG code's internal virtual-array mechanisms.
 */



/******************** JPEG DECOMPRESSION SAMPLE INTERFACE *******************/

/* This half of the example shows how to read data from the JPEG decompressor.
 * It's a bit more refined than the above, in that we show:
 *   (a) how to modify the JPEG library's standard error-reporting behavior;
 *   (b) how to allocate workspace using the library's memory manager.
 *
 * Just to make this example a little different from the first one, we'll
 * assume that we do not intend to put the whole image into an in-memory
 * buffer, but to send it line-by-line someplace else.  We need a one-
 * scanline-high JSAMPLE array as a work buffer, and we will let the JPEG
 * memory manager allocate it for us.  This approach is actually quite useful
 * because we don't need to remember to deallocate the buffer separately: it
 * will go away automatically when the JPEG object is cleaned up.
 */


/*
 * ERROR HANDLING:
 *
 * The JPEG library's standard error handler (jerror.c) is divided into
 * several "methods" which you can override individually.  This lets you
 * adjust the behavior without duplicating a lot of code, which you might
 * have to update with each future release.
 *
 * Our example here shows how to override the "error_exit" method so that
 * control is returned to the library's caller when a fatal error occurs,
 * rather than calling exit() as the standard error_exit method does.
 *
 * We use C's setjmp/longjmp facility to return control.  This means that the
 * routine which calls the JPEG library must first execute a setjmp() call to
 * establish the return point.  We want the replacement error_exit to do a
 * longjmp().  But we need to make the setjmp buffer accessible to the
 * error_exit routine.  To do this, we make a private extension of the
 * standard JPEG error handler object.  (If we were using C++, we'd say we
 * were making a subclass of the regular error handler.)
 *
 * Here's the extended error handler struct:
 */

struct my_error_mgr {
  struct jpeg_error_mgr pub;	/* "public" fields */

  jmp_buf setjmp_buffer;	/* for return to caller */
};

typedef struct my_error_mgr * my_error_ptr;

/*
 * Here's the routine that will replace the standard error_exit method:
 */

METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{
  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
  my_error_ptr myerr = (my_error_ptr) cinfo->err;

  /* Always display the message. */
  /* We could postpone this until after returning, if we chose. */
  (*cinfo->err->output_message) (cinfo);

  /* Return control to the setjmp point */
  longjmp(myerr->setjmp_buffer, 1);
}


/*
 * Sample routine for JPEG decompression.  We assume that the source file name
 * is passed in.  We want to return 1 on success, 0 on error.
 */


GLOBAL(int)
read_JPEG_file (char * filename)
{
  /* This struct contains the JPEG decompression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   */
  struct jpeg_decompress_struct cinfo;
  /* We use our private extension JPEG error handler.
   * Note that this struct must live as long as the main JPEG parameter
   * struct, to avoid dangling-pointer problems.
   */
  struct my_error_mgr jerr;
  /* More stuff */
  FILE * infile;		/* source file */
  JSAMPARRAY buffer;		/* Output row buffer */
  int row_stride;		/* physical row width in output buffer */

  /* In this example we want to open the input file before doing anything else,
   * so that the setjmp() error recovery below can assume the file is open.
   * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
   * requires it in order to read binary files.
   */

  if ((infile = fopen(filename, "rb")) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    return 0;
  }

  /* Step 1: allocate and initialize JPEG decompression object */

  /* We set up the normal JPEG error routines, then override error_exit. */
  cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = my_error_exit;
  /* Establish the setjmp return context for my_error_exit to use. */
  if (setjmp(jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error.
     * We need to clean up the JPEG object, close the input file, and return.
     */
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    return 0;
  }
  /* Now we can initialize the JPEG decompression object. */
  jpeg_create_decompress(&cinfo);

  /* Step 2: specify data source (eg, a file) */

  jpeg_stdio_src(&cinfo, infile);

  /* Step 3: read file parameters with jpeg_read_header() */

  (void) jpeg_read_header(&cinfo, TRUE);
  /* We can ignore the return value from jpeg_read_header since
   *   (a) suspension is not possible with the stdio data source, and
   *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
   * See libjpeg.doc for more info.
   */

  /* Step 4: set parameters for decompression */

  /* In this example, we don't need to change any of the defaults set by
   * jpeg_read_header(), so we do nothing here.
   */

  /* Step 5: Start decompressor */

  (void) jpeg_start_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* We may need to do some setup of our own at this point before reading
   * the data.  After jpeg_start_decompress() we have the correct scaled
   * output image dimensions available, as well as the output colormap
   * if we asked for color quantization.
   * In this example, we need to make an output work buffer of the right size.
   */ 
  /* JSAMPLEs per row in output buffer */
  row_stride = cinfo.output_width * cinfo.output_components;
  /* Make a one-row-high sample array that will go away when done with image */
  buffer = (*cinfo.mem->alloc_sarray)
		((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

  /* Step 6: while (scan lines remain to be read) */
  /*           jpeg_read_scanlines(...); */

  /* Here we use the library's state variable cinfo.output_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   */
  while (cinfo.output_scanline < cinfo.output_height) {
    /* jpeg_read_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could ask for
     * more than one scanline at a time if that's more convenient.
     */
    (void) jpeg_read_scanlines(&cinfo, buffer, 1);
    /* Assume put_scanline_someplace wants a pointer and sample count. */
    put_scanline_someplace(buffer[0], row_stride);
  }

  /* Step 7: Finish decompression */

  (void) jpeg_finish_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* Step 8: Release JPEG decompression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_decompress(&cinfo);

  /* After finish_decompress, we can close the input file.
   * Here we postpone it until after no more JPEG errors are possible,
   * so as to simplify the setjmp error logic above.  (Actually, I don't
   * think that jpeg_destroy can do an error exit, but why assume anything...)
   */
  fclose(infile);

  /* At this point you may want to check to see whether any corrupt-data
   * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
   */

  /* And we're done! */
  return 1;
}


/*
 * SOME FINE POINTS:
 *
 * In the above code, we ignored the return value of jpeg_read_scanlines,
 * which is the number of scanlines actually read.  We could get away with
 * this because we asked for only one line at a time and we weren't using
 * a suspending data source.  See libjpeg.doc for more info.
 *
 * We cheated a bit by calling alloc_sarray() after jpeg_start_decompress();
 * we should have done it beforehand to ensure that the space would be
 * counted against the JPEG max_memory setting.  In some systems the above
 * code would risk an out-of-memory error.  However, in general we don't
 * know the output image dimensions before jpeg_start_decompress(), unless we
 * call jpeg_calc_output_dimensions().  See libjpeg.doc for more about this.
 *
 * Scanlines are returned in the same order as they appear in the JPEG file,
 * which is standardly top-to-bottom.  If you must emit data bottom-to-top,
 * you can use one of the virtual arrays provided by the JPEG memory manager
 * to invert the data.  See wrbmp.c for an example.
 *
 * As with compression, some operating modes may require temporary files.
 * On some systems you may need to set up a signal handler to ensure that
 * temporary files are deleted if the program is interrupted.  See libjpeg.doc.
 */

 

 

[\cc]

转载:Xlib库使用基础

Basic Graphics Programming With The Xlib Library
http://users.actcom.co.il/~choo/lupg/tutorials/xlib-programming/xlib-programming.html
1.  Preface
2. The Client And Server Model Of The X Window System
3. GUI programming - the Asynchronous Programming Model
4. Basic Xlib Notions
1. The X Display
2. The GC - Graphics Context
3. Object Handles
4. Memory Allocation For Xlib Structures
5. Events
5. Compiling Xlib-Based Programs
6. Opening And Closing The Connection To An X Server
7. Checking Basic Information About A Display
8. Creating A Basic Window - Our "hello world" Program
9. Drawing In A Window
1. Allocating A Graphics Context (GC)
2. Drawing Primitives - Point, Line, Box, Circle...
10. X Events
1. Registering For Event Types Using Event Masks
2. Receiving Events - Writing The Events Loop
3. Expose Events
4. Getting User Input
1. Mouse Button Click And Release Events
2. Mouse Movement Events
3. Mouse Pointer Enter And Leave Events
4. The Keyboard Focus
5. Keyboard Press And Release Events
5. X Events - A Complete Example
11. Handling Text And Fonts
1. The Font Structure
2. Loading A Font
3. Assigning A Font To A Graphics Context
4. Drawing Text In A Window
12. Windows Hierarchy
1. Root, Parent And Child Windows
2. Events Propagation
13. Interacting With The Window Manager
1. Window Properties
2. Setting The Window Name And Icon Name
3. Setting Preferred Window Size(s)
4. Setting Miscellaneous Window Manager Hints
5. Setting An Application's Icon
14. Simple Window Operations
1. Mapping And UN-Mapping A Window
2. Moving A Window Around The Screen
3. Resizing A Window
4. Changing Windows Stacking Order - Raise And Lower
5. Iconifying And De-Iconifying A Window
6. Getting Info About A Window
15. Using Colors To Paint The Rainbow
1. Color Maps
2. Allocating And Freeing Color Maps
3. Allocating And Freeing A Color Entry
4. Drawing With A Color
16. X Bitmaps And Pixmaps
1. What Is An X Bitmap? An X Pixmap?
2. Loading A Bitmap From A File
3. Drawing A Bitmap In A Window
4. Creating A Pixmap
5. Drawing A Pixmap In A Window
6. Freeing A Pixmap
17. Messing With The Mouse Cursor
1. Creating And Destroying A Mouse Cursor
2. Setting A Window's Mouse Cursor
--------------------------------------------------------------------------------
Preface
This tutorial is the first in a series of "would-be" tutorials about graphical programming in the X window environment. By itself, it is useless. A real X programmer usually uses a much higher level of abstraction, such as using Motif (or its free version, lesstiff), GTK, QT and similar libraries. However, we need to start somewhere. More than this, knowing how things work down below is never a bad idea.
After reading this tutorial, one would be able to write very simple graphical programs, but not programs with a descent user interface. For such programs, one of the previously mentioned libraries would be used.
--------------------------------------------------------------------------------
X窗口系统的客户/服务器模式
当初开发X窗口系统的主要目的只有一个,那就是灵活性。这个灵活性的意思就是说一件东西虽然看起来是在这工作,但却实际上是工作在很远的地方。因此,较低 等级的实现部分就必须提供绘制窗口,处理用户输入,画画,使用颜色等工作的工具。在这个要求下,决定了系统被分成了两部分,客户端和服务器端。客户端决定 做什么,服务器端执行真正的绘图和接受用户的输入并把它发给客户端。
这种模式与我们一般习惯的客户端和服务器端的概念是正好相反的。在我们的情况下,用户就坐在服务器端控制的机器前,而客户端这时却是运行在远程主机上。服 务器端控制着显示屏,鼠标和键盘。一个客户端也许正连接着服务器端,要求给它画一个窗口(或者是一堆),并要求服务器端把用户对它的窗口的输入传给它。结 果,好几个客户端可能连接到了一个服务器端上-有的在运行一个电子邮件软件,有的在运行一个网页浏览器等。当用户输入了指令给窗口,服务器端就会把指令打 包成事件传给控制那个窗口的客户端,客户端根据接受到的事件决定干什么然后发送请求让服务器端去画什么。
以上介绍的会话都是通过X消息协议传输的。该协议是实现在TCP/IP协议上的,它允许在一个网络里的客户端访问这个网络里的任何服务器端。最后,X服务 器端可以和客户端运行在同一台机器上以获得性能优化(注意,一个X协议事件可能会达到上百KB),例如使用共享内存,或者使用Unix域socket(在 一个Unix系统的两个进程间创建一个本地通道进行通信的方法)。
--------------------------------------------------------------------------------
图形用户接口(GUI)编程-异步编程模式
不像我们通常的令人愉快的程序,一个GUI程序通常使用异步编程模式,也就是下面要介绍的"事件驱动编程"。这个"事件驱动编程"的意思是说程序通常都处 于空闲状态,等待从X服务器发来的事件,等收到了事件,才根据事件做相应的事情。一个事件可能是"用户在屏幕某处x,y点击了鼠标左键",或者是"你控制 的窗口需要被重画"。因为程序要回应用户的请求,同时还需要刷新自己的请求队列,因此需要程序尽可能使用较短的事件来处理一个事件(例如,作为一条公认的 准则,不能超过200毫秒)。
这也暗示着当然存在需要程序处理很长时间才能完成的事件(例如一个到远程服务器的网络连接,或者是连接一个数据库,或者是不幸的要处理一个超大文件的复制 工作)。这都要求程序使用异步方式来处理而不是通常的同步方式。这时候就应该采用各种各样的异步编程方法来进行这些耗时的工作了,或者干脆把它们交给一个 线程或进程来进行。
根据以上的说明,一个GUI程序就应该像以下的方式来工作:
进行初始化工作
连接X服务器
进行与X相关的初始化工作
进行循环
从X服务器那里接受下一个事件
根据收到的事件发送各种绘图指令给X服务器
如果事件是个退出事件,结束循环
关闭与X服务器的连接
进行资源释放工作
--------------------------------------------------------------------------------
Xlib的基本思想
X协议是非常复杂的,为了大家不用再辛辛苦苦把时间浪费在实现它上面,就有了一个叫"Xlib"的库。这个库提供了访问任何X服务器的非常底层的手段。因 为X协议已经被标准化了,理论上客户程序使用任何Xlib的实现都可以访问任何X服务器。在今天,这看起来可能很琐碎,但如果回到那个使用字符终端和专有 绘图方法的时代,这应该是一个很大的突破吧。实际上,你很快发现围绕瘦客户机,窗口终端服务器等领域会有许多多么令人兴奋的事情。
--------------------------------------------------------------------------------
X显示
使用XLib的基本思想就是X显示。它代表了一个打开的到X服务器的连接的结构。它隐藏了一个保存有从X服务器来的事件的队列,和一个保存客户程序准备发往服务器的请求队列。在Xlib里,这个结构被命名为显示"Display"。
当我们打开了一个到X服务器的连接,库就会返回一个指向这个结构的指针。然后,我们就可以使用这个指针来使用Xlib里各种各样的函数。
--------------------------------------------------------------------------------
GC - 图形上下文
当我们进行各种绘图操作(图形,文本等)的时候,我们也许会使用许多参数来指定如何绘制,前景,背景,使用什么颜色,使用什么字体等等,等等。为了避免为 每个绘图函数设置数量惊人的参数,我们使用一个叫"GC"的图形上下文结构。我们在这个结构里设置各种绘图参数,然后传给绘图函数就行了。这应当是一个非 常方便的方法吧,尤其当我们在进行一连串操作中使用相同的参数时。
--------------------------------------------------------------------------------
对象句柄
当X服务器为我们创建了各种各样的对象的时候 - 例如窗口,绘图区和光标 - 相应的函数就会返回一个句柄。这是一个存在在X服务器空间中的对象的一个标识-而不是在我们的应用程序的空间里。在后面我们就可以使用Xlib的函数通过 句柄来操纵这些对象。X服务器维护了一个实际对象到句柄的映射表。Xlib提供了各种类型来定义这些对象。虽然这些类型实际上只是简单的整数,但我们应该 继续使用这些类型的名字 - 理由是为了可移植。
--------------------------------------------------------------------------------
Xlib结构的内存分配
Xlib的接口使用了各种类型的结构。有些可以由用户直接来分配内存,有些则只能使用专门的Xlib库函数来分配。在使用库来分配的情况,库会生成有适当 初始参数的结构。这对大家来说是非常方便的,指定初始值对于不太熟练的程序员来说是非常头疼的。记住-Xlib想要提供非常灵活的功能,这也就意味着它也 会变得非常复杂。提供初始值设置的功能将会帮助那些刚开始使用X的程序员们,同时不会干扰那些高高手们。
在释放内存时,我们使用与申请的同样方法来释放(例如,使用free()来释放malloc()申请的内存)。所以,我们必须使用XFree()来释放内存。
事件
一个叫"XEvent"的结构来保存从X服务器那里接受到的事件。Xlib提供了非常大量的事件类型。XEvent包括事件的类型,以及与事件相关的数据 (例如在屏幕什么地方生成的事件,鼠标键的事件等等),因此,要根据事件类型来读取相应的事件里的数据。这时,XEvent结构使用c语言里的联合来保存 可能的数据(如果你搞不清楚c的联合是怎么回事,那你就得花点时间再读读你的教科书了)。结果,我们就可能受到XExpose事件,一个XButton事 件,一个XMotion事件等等。
--------------------------------------------------------------------------------
编译基于Xlib的程序
编译基于Xlib的程序需要与Xlib库连接。可以使用下面的命令行:
cc prog.c -o prog -lX11
如果编译器报告找不到X11库,可以试着加上"-L"标志,像这样:
cc prog.c -o prog -L/usr/X11/lib -lX11
或者这样(针对使用X11的版本6)
cc prog.c -o prog -L/usr/X11R6/lib -lX11
在SunOs 4 系统上,X的库被放到了 /usr/openwin/lib
cc prog.c -o prog -L/usr/openwin/lib -lX11
等等,具体情况具体分析
--------------------------------------------------------------------------------
打开,关闭到一个X服务器的连接
一个X程序首先要打开到X服务器的连接。我们需要指定运行X服务器的主机的地址,以及显示器编号。X窗口允许一台机器开多个显示。然而,通常只有一个编号 为"0"的显示。如果我们想要连接本地的显示(例如进行显示的机器同时又是客户程序运行的机器),我们可以直接使用":0"来连接。现在我们举例,连接一 台地址是"simey"的机器的显示,我们可以使用地址"simey:0",下面演示如何进行连接
#include  /* defines common Xlib functions and structs. */
.
.
/* this variable will contain the pointer to the Display structure */
/* returned when opening a connection. */
Display* display;
/* open the connection to the display "simey:0". */
display = XOpenDisplay("simey:0");
if (display == NULL) {
fprintf(stderr, "Cannot connect to X server %s\n", "simey:0");
exit (-1);
}
注意,通常要为X程序检查是否定义了环境变量"DISPLAY",如果定义了,可以直接使用它来作为XOpenDisplay()函数的连接参数。
当程序完成了它的工作且需要关闭到X服务器的连接,它可以这样做:
XCloseDisplay(display);
这会使X服务器关闭所有为我们创建的窗口以及任何在X服务器上申请的资源被释放。当然,这并不意味着我们的客户程序的结束。
--------------------------------------------------------------------------------
检查一个显示的相关基本信息
一旦我们打开了一个到X服务器的连接,我们应该检查与它相关的一些基本信息:它有什么样的屏幕,屏幕的尺寸(长和宽),它支持多少颜色(黑色和白色?灰度 级?256色?或更多),等等。我们将演示一些有关的操作。我们假设变量"display"指向一个通过调用XOpenDisplay()获得的显示结 构。
/* this variable will be used to store the "default" screen of the */
/* X server. usually an X server has only one screen, so we&#39;re only */
/* interested in that screen. */
int screen_num;
/* these variables will store the size of the screen, in pixels. */
int screen_width;
int screen_height;
/* this variable will be used to store the ID of the root window of our */
/* screen. Each screen always has a root window that covers the whole */
/* screen, and always exists. */
Window root_window;
/* these variables will be used to store the IDs of the black and white */
/* colors of the given screen. More on this will be explained later. */
unsigned long white_pixel;
unsigned long black_pixel;
/* check the number of the default screen for our X server. */
screen_num = DefaultScreen(display);
/* find the width of the default screen of our X server, in pixels. */
screen_width = DisplayWidth(display, screen_num);
/* find the height of the default screen of our X server, in pixels. */
screen_height = DisplayHeight(display, screen_num);
/* find the ID of the root window of the screen. */
root_window = RootWindow(display, screen_num);
/* find the value of a white pixel on this screen. */
white_pixel = WhitePixel(display, screen_num);
/* find the value of a black pixel on this screen. */
black_pixel = BlackPixel(display, screen_num);
还有很多其它的宏来帮助我们获取显示的信息,你可以在Xlib里的参考里找到。另外还有很多相当的函数可以完成相同的工作。
--------------------------------------------------------------------------------
创建一个基本的窗口 - 我们的"Hello world"程序
在我们获得一些窗口的基本信息之后,我们就可以开始创建我们的第一个窗口了。Xlib支持好几个函数来创建窗口,它们其中的一个是XCreateSimpleWindow()。这个函数使用很少的几个参数来指定窗口的尺寸,位置等。以下是它完整的参数列表:
Display* display
指向显示结构的指针
Window parent
新窗口的父窗口的ID。
int x
窗口的左上X坐标(单位为屏幕像素)
int y
窗口的左上Y坐标(单位为屏幕像素)
unsigned int width
窗口的宽度(单位为屏幕像素)
unsigned int height
窗口的高度(单位为屏幕像素)
unsigned int border_width
窗口的边框宽度(单位为屏幕像素)
unsigned long border
用来绘制窗口边框的颜色
unsigned long background
用来绘制窗口背景的颜色
让我们创建一个简单的窗口,它的宽度是屏幕宽的1/3,高度是屏幕高的1/3,背景色是白色,边框是黑色,边框的宽度是2个像素。该窗口将会被放置到屏幕的左上角。
/* this variable will store the ID of the newly created window. */
Window win;
/* these variables will store the window&#39;s width and height. */
int win_width;
int win_height;
/* these variables will store the window&#39;s location. */
int win_x;
int win_y;
/* calculate the window&#39;s width and height. */
win_width = DisplayWidth(display, screen_num) / 3;
win_height = DisplayHeight(display, screen_num) / 3;
/* position of the window is top-left corner - 0,0. */
win_x = win_y = 0;
/* create the window, as specified earlier. */
win = XCreateSimpleWindow(display,
RootWindow(display, screen_num),
win_x, win_y,
win_width, win_height,
win_border_width, BlackPixel(display, screen_num),
WhitePixel(display, screen_num));
事实上我们创建窗口并不意味着它将会被立刻显示在屏幕上,在缺省情况下,新建的窗口将不会被映射到屏幕上,它们是不可见的。为了能让我们创建的窗口能被显示到屏幕上,我们使用函数XMapWindow():
XMapWindow(win);
如果想察看目前为止我们所举的例子的代码,请参看源程序simple-window.c。你将会发现两个新的函数 -
XFlush() 和XSync()。函数XFlush()刷新所有处于等待状态的请求到X服务器 - 非常像函数fflush()刷新所有的内容到标准输出。XSync()也刷新所有处于等待状态的请求,接着等待X服务器处理完所有的请求再继续。在一个一 般的程序里这不是必须的(据此你可以发现我们什么时候只是写一个一般的程序),但我们现在把它们提出来,尝试在有或没有这些函数的情况下程序不同的行为。
--------------------------------------------------------------------------------
在窗口里绘制
在窗口里绘图可以使用各种绘图函数 - 画点,线,圈,矩形,等等。为了能在一个窗口里绘图,我们首先需要定义各种参数 - 如线的宽度,使用什么颜色,等等。这都需要使用一个图形上下文(GC)。
--------------------------------------------------------------------------------
申请一个图形上下文(GC)
如我们已经提到的,一个图形上下文定义一些参数来使用绘图函数。因此,为了绘制不同的风格,我们可以在一个窗口里使用多个图形上下文。使用函数 XCreateGC()可以申请到一个新的图形上下文,如以下例(在这段代码里,我们假设"display"指向一个显示结构,"win"是当前创建的一 个窗口的ID):
/* this variable will contain the handle to the returned graphics context. */
GC gc;
/* these variables are used to specify various attributes for the GC. */
/* initial values for the GC. */
XGCValues values = CapButt | JoinBevel;
/* which values in &#39;values&#39; to check when creating the GC. */
unsigned long valuemask = GCCapStyle | GCJoinStyle;
/* create a new graphical context. */
gc = XCreateGC(display, win, valuemask, &values);
if (gc  0)
break;
/* ok, now draw the line... */
XDrawLine(display, win, gc, 0, 100, 400, 100);
break;
--------------------------------------------------------------------------------
获取用户输入
就目前来说,用户的输入主要从两个地方过来 - 鼠标和键盘。有各种各样的事件帮助我们来获取用户的输入 - 一个键盘上的键被按下了,一个键盘上的键被松开了,鼠标光标离开了我们的窗口,鼠标光标进入了我们的窗口等等。
--------------------------------------------------------------------------------
鼠标按键事件和松开事件
我们为我们的窗口处理的第一个事件是鼠标按钮时间。为了注册一个这样的事件类型,我们将追加以下的面具
ButtonPressMask
通知我们窗口中的任何一个鼠标键按下动作
ButtonReleaseMask
通知我们窗口中的任何一个鼠标键松开动作
在我们的事件循环中通过switch来检查以下的事件类型
ButtonPress
在我们的窗口上一个鼠标键被按下了
ButtonRelease
在我们的窗口上一个鼠标键被松开了
在事件结构里,通过"an_event.xbutton"来获得事件的类型,另外它还包括下面这些有趣的内容:
Window window
事件发送的目标窗口的ID(如果我们为多个窗口注册了事件)
int x, y
从窗口的左上坐标算起,鼠标键按下时光标在窗口中的坐标
int button
鼠标上那个标号的按钮被按下了,值可能是Button1,Button2,Button3
Time time
事件被放进队列的时间。可以被用来实现双击的处理下面的例子,将演示我们如何在鼠标的位置画点,无论我们何时收到编号为1的按钮的"鼠标按下"的事件时我 们画一个黑点,收到编号为2的按钮的"鼠标按下"的事件时我们擦掉那个黑点(例如画一个白点)。我们假设现在有两个GC,gc_draw使用下面的代码
case ButtonPress:
/* store the mouse button coordinates in &#39;int&#39; variables. */
/* also store the ID of the window on which the mouse was */
/* pressed. */
x = an_event.xbutton.x;
y = an_event.xbutton.y;
the_win = an_event.xbutton.window;
/* check which mouse button was pressed, and act accordingly. */
switch (an_event.xbutton.button) {
case Button1:
/* draw a pixel at the mouse position. */
XDrawPoint(display, the_win, gc_draw, x, y);
break;
case Button2:
/* erase a pixel at the mouse position. */
XDrawPoint(display, the_win, gc_erase, x, y);
break;
default: /* probably 3rd button - just ignore this event. */
break;
}
break;
--------------------------------------------------------------------------------
鼠标光标的进入和离开事件
另一个程序通常会感兴趣的事件是,有关鼠标光标进入一个窗口的领域以及离开那个窗口的领域的事件。有些程序利用该事件来告诉用户程序现在在焦点里面。为了注册这种事件,我们将会在函数XSelectInput()里注册几个面具。
EnterWindowMask
通知我们鼠标光标进入了我们的窗口中的任意一个
LeaveWindowMask
通知我们鼠标光标离开了我们的窗口中的任意一个
我们的事件循环中的分支检查将检查以下的事件类型
EnterNotify
鼠标光标进入了我们的窗口
LeaveNotify
鼠标光标离开了我们的窗口
这些事件类型的数据结构通过例如"an_event.xcrossing"来访问,它还包含以下有趣的成员变量:
Window window
事件发送的目标窗口的ID(如果我们为多个窗口注册了事件)
Window subwindow
在一个进入事件中,它的意思是从那个子窗口进入我们的窗口,在一个离开事件中,它的意思是进入了那个子窗口,如果是"none",它的意思是从外面进入了我们的窗口。
int x, y
从窗口的左上坐标算起,事件产生时鼠标光标在窗口中的坐标
int mode
鼠标上那个标号的按钮被按下了,值可能是Button1,Button2,Button3
Time time
事件被放进队列的时间。可以被用来实现双击的处理
unsigned int state
这个事件发生时鼠标按钮(或是键盘键)被按下的情况 - 如果有的话。这个成员使用按位或的方式来表示
Button1Mask
Button2Mask
Button3Mask
Button4Mask
ShiftMask
LockMask
ControlMask
Mod1Mask
Mod2Mask
Mod3Mask
Mod4Mask
它们的名字是可以扩展的,当第五个鼠标钮被按下时,剩下的属性就指明其它特殊键(例如Mod1一般是"ALT"或者是"META"键)
Bool focus
当值是True的时候说明窗口获得了键盘焦点,False反之
--------------------------------------------------------------------------------
键盘焦点
在屏幕上同时会有很多窗口,但同一时间只能有一个窗口获得键盘的使用。X服务器是如何知道哪一个窗口可以发送键盘事件呢?这个是通过使用键盘焦点来实现 的。在同一时间只能有一个窗口获得键盘焦点。Xlib函数里存在函数允许程序让指定窗口获得键盘焦点。用户通常使用窗口管理器来为窗口设置焦点(通常是点 击窗口的标题栏)。
一旦我们的窗口获得了键盘焦点,每个键的按下和松开都将引起服务器发送事件给我们的程序(如果已经注册了这些事件的类型)。
--------------------------------------------------------------------------------
键盘键按下和松开事件
如果我们程序控制的窗口获得了键盘焦点,它就可以接受按键的按下和松开事件。为了注册这些事件的类型,我们就需要通过函数XSelectInput()来注册下面的面具。
KeyPressMask
通知我们的程序什么时候按键被按下了
KeyPressMask
通知我们的程序什么时候按键被松开了
我们的事件循环中的分支检查将检查以下的事件类型
Window window
事件发送的目标窗口的ID(如果我们为多个窗口注册了事件)
unsigned int keycode
被按下(或松开)的键的编码。这是一些X内部编码,应该被翻译成一个键盘键符号才能方便使用,将会在下面介绍。
int x, y
从窗口的左上坐标算起,事件产生时鼠标光标在窗口中的坐标
Time time
事件被放进队列的时间。可以被用来实现双击的处理
unsigned int state
这个事件发生时鼠标按钮(或是键盘键)被按下的情况 - 如果有的话。这个成员使用按位或的方式来表示
Button1Mask
Button2Mask
Button3Mask
Button4Mask
ShiftMask
LockMask
ControlMask
Mod1Mask
Mod2Mask
Mod3Mask
Mod4Mask
它们的名字是可以扩展的,当第五个鼠标钮被按下时,剩下的属性就指明其它特殊键(例如Mod1一般是"ALT"或者是"META"键)
如我们前面所提到的,按键编码对我们来说是没有什么意义的,它是由连接着X服务器的键盘产生的硬件级编码并且是与某个型号的键盘相关的。为了能解释到底是 哪个按键产生的事件,我们把它翻译成已经被标准化了的按键符号。我们可以使用函数XKeycodeToKeysym()来完成这个翻译工作。该函数使用3 个参数:一个显示的指针,要被翻译的键盘编码,和一个索引(我们在这里使用"0")。标准的Xlib键编码可以参考文 件"X11/keysymdef.h"。在下面的例子里我们使用函数XkeycodeToKeysym来处理按键操作,我们讲演示如何以以下顺序处理按键 事件:按"1"键将会在鼠标的当前位置下画一个点。按下"DEL"键将擦除那个点。按任何字母键(a至z,大写或小写)将在标准输出里打印。其它的按键将 会被无视。假设下面的"case"段代码是在一个消息循环中。
case KeyPress:
/* store the mouse button coordinates in &#39;int&#39; variables. */
/* also store the ID of the window on which the mouse was */
/* pressed. */
x = an_event.xkey.x;
y = an_event.xkey.y;
the_win = an_event.xkey.window;
{
/* translate the key code to a key symbol. */
KeySym key_symbol = XKeycodeToKeysym(display, an_event.xkey.keycode, 0);
switch (key_symbol) {
case XK_1:
case XK_KP_1: /* &#39;1&#39; key was pressed, either the normal &#39;1&#39;, or */
/* the &#39;1&#39; on the keypad. draw the current pixel. */
XDrawPoint(display, the_win, gc_draw, x, y);
break;
case XK_Delete: /* DEL key was pressed, erase the current pixel. */
XDrawPoint(display, the_win, gc_erase, x, y);
break;
default: /* anything else - check if it is a letter key */
if (key_symbol >= XK_A && key_symbol = XK_a && key_symbol fid);
"fid"领域是一个XFontStruct结构用来为各种请求识别各种装载的字体。
--------------------------------------------------------------------------------
在一个窗口中绘制文本
我们一旦为我们的GC装载了字体,我们就可以使用例如函数XDrawString(),在我们的窗口里绘制文本。该函数可以在窗口里的一个给定位置里绘制文本。给定的位置将是从被绘制的文本的左下算起,下面是它的例子:
/* assume that win_width contains the width of our window, win_height */
/* contains the height of our window, and &#39;win&#39; is the handle of our window. */
/* some temporary variables used for the drawing. */
int x, y;
/* draw a "hello world" string on the top-left side of our window. */
x = 0;
y = 0;
XDrawString(display, win, gc, x, y, "hello world", strlen("hello world"));
/* draw a "middle of the road" string in the middle of our window. */
char* text_string = "middle of the road";
/* find the width, in pixels, of the text that will be drawn using */
/* the given font. */
int string_width = XTextWidth(font_info, text_string, strlen(text_string));
/* find the height of the characters drawn using this font. */
int fond_height = font_info->ascent + font_info->descent;
x = (win_width - string_width) / 2;
y = (win_width - font_height) / 2;
XDrawString(display, win, gc, x, y, "hello world", strlen("hello world"));
以下的说明应该可以使程序更清楚:
函数XTextWidth()被用来预测字符串的长度,当它使用指定字体进行绘制时。它被用来检查那里是开始那里是结束使它看起来占据着窗口的中央
一个字体的两个名字为"ascent"和"descent"的属性用来指定字体的高。基本上,一个字体的字符是画在一条假象的横线上的。一些字符被画在横 线上面,一些画在下面。最高的字符是被画在"font_info->ascent"线上的,最低的部分则 在"font_info->descent"线下面。因此,这两个值得和指明了字体的高度。
上面的源程序可以参考文件simple-text.c
X窗口的组织体系
当窗口被显示在X服务器上时,它们通常按照一定组织体系来排序 - 每个窗口可以有子窗口,每个子窗口又可以有自己的子窗口。让我们来查看这个组织体系的一些特性,看看它们是如何来影响例如绘画和事件等处理。
根窗口,父窗口和子窗口
每一个屏幕上都有一个根窗口。根窗口总是占据整个屏幕尺寸。这个窗口无法被销毁,改变尺寸或者图标化。当一个应用程序创建了一些窗口,它先创建至少一个顶 层窗口。在被映射到屏幕上后,这个窗口成为一个根窗口的直接子窗口。这个窗口在被映射到屏幕上之前,窗口管理器被告知什么发生了,然后,窗口管理器获得特 权成为新顶层窗口的"父亲"。这通常被用来增加一个会包含新窗口的窗口和绘制框架,标题栏,系统菜单等。
一旦一个顶层窗口(当然它实际上不是一个顶层窗口,因为窗口管理器已经成为它的父窗口了)被创建了,应用程序可以在它里面创建它的子窗口。一个子窗口只能 在它的父窗口里显示 - 如果试图把它移动到外面,出去的部分将被父窗口的边框给切掉。任何窗口都可以包含一个以上的子窗口,在这种情况下,这些子窗口将被放置在应用的内部栈上。 当一个顶层窗口被打开 - 它的所有子窗口也将随着它被打开。
以下例子演示如何在一个给定的叫"win"的窗口里打开一个子窗口。
Lets see how to create a child window inside a given window ''win''.
/* this variable will store the handle of the newly created child window. */
Window child_win;
/* these variables will store the window''s width and height. */
int child_win_width = 50;
int child_win_height = 30;
/* these variables will store the window''s location. */
/* position of the child window is top-left corner of the */
/* parent window. - 0,0.  */
int child_win_x = 0;
int child_win_y = 0;
/* create the window, as specified earlier. */
child_win = XCreateSimpleWindow(display, win, child_win_x, child_win_y,                                          child_win_width, child_win_height,  child_win_border_width,                                          BlackPixel(display, screen_num),
WhitePixel(display, screen_num));
事件传递
先前我们已经讨论了事件传递 - 如果一个窗口收到了一个它不处理的事件 - 它就把该事件发到它的父窗口去。如果那个父窗口也不处理该事件 - 那个父窗口就把该事件发到它的父窗口上去,接下来依此类推。这种行为对一个简单的Xlib程序是没什么用的,但对于抽象级更高的绘图库是有用的。这些抽象 级更高的绘图库通常把某个特定窗口的事件联系到一个函数上去。在这种情况下,发送事件到特定的窗口并用适当的函数来处理就非常有用。
与窗口管理器进行交互
在我们察看了如何创建和绘制窗口之后,我们回过头来看一下我们的窗口是如何与它们的环境 - 整个屏幕和其它窗口进行交互的。首先,我们的程序需要与窗口管理器进行交互。窗口管理器有责任装饰被绘制的窗口(例如增加框架,一个图标化的按钮,一个系 统菜单,一个标题栏),同时在窗口被图标化时绘制图标。它还管理屏幕里的窗口排列顺序以及其它可管理的任务。我们需要给它各种提示以让它以我们需要的方式 来对待我们的窗口。
窗口属性
许多与窗口管理器交流的参数都通过叫"properties"的数据来传递。X服务器把这些属性贴到各种窗口上,同时把它们存储成一种可以被各种架构的系 统所能读取的格式(记住,一个X客户程序可能运行在一台远程主机上)。属性可以是各种类型 - 数字,字符串,等等。大部分的窗口管理器提示函数使用文本属性。一个叫XStringListToTextProperty()的函数可以把C语言的字符 串转换成X文本属性,转换后的结果就可以传给各色Xlib函数。以下是一个例子:
/* This variable will store the newly created property. */
XTextProperty window_title_property;
/* This is the string to be translated into a property. */
char* window_title = "hello, world";
/* translate the given string into an X property. */
int rc = XStringListToTextProperty(&window_title, 1, &window_title_property);
/* check the success of the translation. */
if (rc == 0) { fprintf(stderr, "XStringListToTextProperty - out of memory\n");
exit(1);}
函数XStringListToTextProperty()接收一个C字符串矩阵(在我们的例子里只有一个)和一个指向XTextProperty型变 量的指针为参数,合并C字符串里的属性把值传到XTextProperty型变量里。成功时它返回一个非0值,失败时返回0(例如,没有足够的内存来完成 操作)。
设置窗口名字和图标名字
我们需要做的第一件事就是给我们的窗口设置名字。使用函数XSetWMName()。窗口管理器也许会把这个名字显示在窗口标题栏或是在任务栏上。该函数 接受3个参数:一个指向显示的指针,一个窗口句柄,和一个包含有我们设置的名字的XTextProperty变量。下面是我们如何做的:
/* assume that window_title_property is our XTextProperty, and is */
/* defined to contain the desired window title.                   */
XSetWMName(display, win, &window_title_property);
为了设置我们的窗口的图标化名字,我们将用相同的方式使用函数XSetWMIconName()。
/* this time we assume that icon_name_property is an initialized */
/* XTextProperty variable containing the desired icon name.      */
XSetWMIconName(display, win, &icon_name_property);
设置满意的窗口尺寸
在各种情况下,我们希望让窗口管理器知道我们指定的窗口尺寸以及只允许用户在我们的限定下改变窗口尺寸。例如,一个终端窗口(像xterm),我们总是要 求我们的窗口可以包含全部的行和列,因此我们就不能从中间截断我们的显示。在其它情况下,我们不希望我们的窗口可以被改变尺寸(像绝大部分的对话框窗 口),等等。我们可以依赖窗口管理器的这个尺寸信息,虽然它可能被简单的忽视掉。我们首先需要创建一个数据结构来包含该信息,填充必要的数据,然后使用函 数 XSetWMNormalHints()。下面是如何操作:
/* pointer to the size hints structure. */
XSizeHints* win_size_hints = XAllocSizeHints();
if (!win_size_hints) { fprintf(stderr, "XAllocSizeHints - out of memory\n");
exit(1);}
/* initialize the structure appropriately. */
/* first, specify which size hints we want to fill in. */
/* in our case - setting the minimal size as well as the initial size. */
win_size_hints->flags = PSize | PMinSize;
/* next, specify the desired limits.                             */
/* in our case - make the window''s size at least 300x200 pixels.*/
/* and make its initial size 400x250.                            */
win_size_hints->min_width = 300;win_size_hints->min_height = 200;
win_size_hints->base_width = 400;win_size_hints->base_height = 250;
/* pass the size hints to the window manager. */
XSetWMNormalHints(display, win, win_size_hints);
/* finally, we can free the size hints structure. */
XFree(win_size_hints);
请查看你的手册来获取尺寸提示的完整信息。
设置各种窗口管理器提示
使用函数XSetWMHints()还可以设置许多其它的窗口管理器提示。该函数使用一个XWMHints结构来传递参数给窗口管理器。下面是例子:
/* pointer to the WM hints structure. */
XWMHints* win_hints = XAllocWMHints();
if (!win_hints) {    fprintf(stderr, "XAllocWMHints - out of memory\n");
exit(1);}
/* initialize the structure appropriately. */
/* first, specify which hints we want to fill in. */
/* in our case - setting the state hint as well as the icon position hint. */
win_hints->flags = StateHint | IconPositionHint;
/* next, specify the desired hints data.                         */
/* in our case - make the window''s initial state be iconized,   */
/* and set the icon position to the top-left part of the screen. */
win_hints->initial_state = IconicState;win_hints->icon_x = 0;
win_hints->icon_y = 0;
/* pass the hints to the window manager. */
XSetWMHints(display, win, win_hints);
/* finally, we can free the WM hints structure. */
XFree(win_hints);
请查阅手册以获取全部选项的详细信息。
设置一个程序的图标
在用户图标化了我们的程序的时候,为了让窗口管理器能为我们的程序设置一个图标,我们使用上面提到的函数XSetWMHints。但是,首先我们需要创建 一个包含有图标数据的像素图。X服务器使用像素图来操作图片,将在后面介绍它的详细使用。在这里,我们只是向你展示如何为你的程序设置图标。我们假设你已 经得到了一个X bitmap格式的图标文件。教程为了方便提供了一个图标文件"icon.bmp" ,下面是代码:
/* include the definition of the bitmap in our program. */
#include "icon.bmp";
/* pointer to the WM hints structure. */
XWMHints* win_hints;
/* load the given bitmap data and create an X pixmap containing it. */
Pixmap icon_pixmap = XCreateBitmapFromData(display, win, icon_bitmap_bits,                                           icon_bitmap_width, icon_bitmap_height);
if (!icon_pixmap) {
fprintf(stderr, "XCreateBitmapFromData - error creating pixmap\n");
exit(1);}
/* allocate a WM hints structure. */
win_hints = XAllocWMHints();
if (!win_hints) {
fprintf(stderr, "XAllocWMHints - out of memory\n");
exit(1);}
/* initialize the structure appropriately. */
/* first, specify which size hints we want to fill in. */
/* in our case - setting the icon''s pixmap. */
win_hints->flags = IconPixmapHint;
/* next, specify the desired hints data.           */
/* in our case - supply the icon''s desired pixmap.*/
win_hints->icon_pixmap = icon_pixmap;
/* pass the hints to the window manager. */
XSetWMHints(display, win, win_hints);
/* finally, we can free the WM hints structure. */
XFree(win_hints);
你可以使用程序例如"xpaint"来创建使用X bitmap格式的文件。
我们提供文件simple-wm-hints.c来总结这一节,这段程序包括创建一个窗口,设置窗口管理器提示为在上面显示,以及一个简单的事件循环。它允许用户调整参数以察看提示是如何影响程序的行为的。这可以帮助你了解X程序的可移植性。
简单窗口操作
对我们的窗口,我们可以做更多的一些事情。例如,改变它们的尺寸,打开或关闭它们,图标化它们等。Xlib提供了一系列函数来完成上面提到的功能。
映射和解除一个窗口的映射
首先我们对窗口作的一对操作是映射它到屏幕上去和解除它的映射。映射一个窗口的操作将会使一个窗口显示在屏幕上,如我们在简单窗口程序例子里所看到的。解 除映射操作将会把窗口从屏幕里移除出去(虽然作为一个逻辑结点它仍然在X服务器里)。这个可以提供产生窗口被隐藏(映射解除)和再显示(映射)的效果。例 如,我们的程序里有一个对话框,我们不需要每次在需要它显示的时候都重新创建一个窗口,我们只是以映射解除的状态创建一次,在用户需要的时候简单的把它映 射到屏幕上去就行了。这比每一次都创建它和销毁它要快多了,当然,这需要在客户端和服务器端同时使用更多的内存。
你应该还记得映射操作是使用函数XMapWindow()。映射解除操作是使用函数XUnmapWindow(),下面是如何使用它们:
/* make the window actually appear on the screen. */
XMapWindow(display, win);
/* make the window hidden. */
XUnmapWindow(display, win);
除非整个窗口被其它窗口给覆盖了,一个暴露事件将在映射操作后发给应用程序。
在屏幕上移动一个窗口
我们想做的另一个操作是在屏幕里移动窗口。使用函数XMoveWindow()可以完成这个操作。它接受窗口的新坐标,使用的方法和函数XCreateSimpleWindow()是一样的。一下是调用的例子:
/* move the window to coordinates x=400 and y=100. */
XMoveWindow(display, win, 400, 100);
注意当窗口移动的时候,窗口的部分可能后被遮住或被重新暴露,这样我们就可能会收到暴露事件。
改变窗口尺寸
接下来我们要做的是改变一个窗口的尺寸。使用函数XResizeWindow()可以完成这个操作:
/* resize the window to width=200 and height=300 pixels. */
XResizeWindow(display, win, 200, 300);
我们可以合并移动和改变尺寸操作为一个操作,使用函数XMoveResizeWindow():
/* move the window to location x=20 y=30, and change its size */
/* to width=100 and height=150 pixels.                        */
XMoveResizeWindow(display, win, 20, 30, 100, 150);
改变窗口们的栈顺序 - 提升和降低
到目前为止我们已经改变了一个单独窗口的许多属性。接下来我们将看看窗口之间的属性。其中一个就是它们的栈属性。也就是说,窗口是如何在屏幕上排列的。最前面的窗口我们说它是在栈顶,最后面的窗口我们说它是在栈底。下面演示我们如何改变窗口的栈顺序:
/* move the given window to the top of the stack. */
XRaiseWindow(display, win1);
/* move the given window to the bottom of the stack. */
XLowerWindow(display, win1);
图标化和恢复一个窗口
在这里我们将要讲解的最后一个操作就是如何把一个窗口变换成图标状态和恢复它。使用函数XIconifyWindow()来把一个窗口变换成图标状态,使 用函数XMapWindow()来恢复它。为了帮助理解为什么图标化函数没有一个对应的反函数,我们必须理解当一个窗口被图标化时,实际发生的事情是那个 窗口被解除映射了,而它的图表被映射了。结果,如果想使哪个窗口在出现,我们只需要简单的映射它一下就行了。图标实际上是另一个窗口,只不过它与我们的窗 口有非常强的联系关系。下面演示如何图标化一个窗口并恢复它:
/* iconify our window. Make its icon window appear on the same */
/* screen as our window (assuming we created our window on the */
/* default screen).                                            */
XIconifyWindow(display, win, DefaultScreen(display));
/* de-iconify our window. the icon window will be automatically */
/* unmapped by this operation.                                  */
XMapWindow(display, win);
获得一个窗口的信息
与可以为窗口设置许多属性相同,我们也可以要求X服务器提供这些属性的值。例如,我们可以检查窗口现在在屏幕里什么位置,当前尺寸,是否被映射了等等。函数XGetWindowAttributes()可以帮助我们获取那些信息:
/* this variable will contain the attributes of the window. */
XWindowAttributes win_attr;
/* query the window''s attributes. */
Status rc = XGetWindowAttributes(display, win, &win_attr);
结构体XWindowAttributes包含了很多数据域,下面是它的一部分:
int x, y;
窗口的位置,相对于它的父窗口。
int width, height;
窗口的宽和高(单位,像素)。
int border_width
窗口的边框宽度
Window root;
根窗口,也就是我们的窗口在那个窗口里被显示了。
这个函数有些问题,就是它返回的是相对于父窗口的位置。这对一些窗口的操作(例如XMoveWindow)是没有什么意义的。为了解决这个问题,我们需要 使用两步的操作。首先,我们找出窗口的父窗口的ID。然后我们在使用它来确定窗口相对于屏幕的坐标。我们使用两个前面没有介绍的函数来完成这个计 算,XQueryTree()和XTranslateCoordinates()。这两个函数的功能超出了我们的需要,所以我们只关注我们需要的:
/* these variables will eventually hold the translated coordinates. */
int screen_x, screen_y;
/* creen_y contain the location of our original */
/* window, using screen coordinates. */
你可以看到Xlib有时候会让我们处理问题时变得很麻烦。
以上的内容可以参考例子window-operations.c 程序。
使用颜色来绘制彩虹
到目前为止,我们的绘制操作都只使用了黑白两色。现在我们就看看如何使用丰富的颜色来绘制。
颜色映射
首先,是没有完全足够的颜色的。屏幕控制器同时只能支持有限的颜色。因此,一个应用程序不能只是要求使用颜色“轻紫红”就盼望这个颜色能被支持。每个应用分配它自己所需要的颜色,如果全部的16或256色入口都已经在使用了,下一个颜色的分配就会失败。
结果,就介绍使用“一个颜色映射”。一个颜色映射是一个与屏幕控制器同时可以支持的颜色数相同的表。每个表中的节点都为每种颜色包含一个RGB(红,绿和 蓝)。当一个应用想在屏幕上绘制时,它并不指定使用什么颜色,而是指定使用映射表里那一个节点,因此,改变表里某个节点的值将会改变程序绘制的颜色。
为了能让程序员使用他想要的颜色来绘制,提供了颜色映射分配函数。你可以要求分配一个颜色映射节点来对应一个RGB值,然后一个节点的索引值返回给你。如 果表满了,这个操作将会失败。你可以接下来要求一个相近的颜色来满足你的需要。这意味着一个相近的颜色将会被绘制到屏幕上去。
在当今的X 服务器使用的现代显示器上,一般都可以支持上百万的颜色,上面那个限制也许看起来挺傻的,但是记住还有很多古旧的显示卡和显示器在被使用。使用颜色映射, 你可以不必考虑服务器的屏幕硬件细节就可以使用它们。在一个支持上百万的显示器上,任何颜色的分配请求都应该会成功。在一个职能支持有限颜色的显示器上可 能会使用一个相近颜色来代替你的要求,这可能不好看,但你的程序仍然能工作。
分配和释放颜色映射
当你使用Xlib绘制的时候,你可以选择屏幕的标准颜色映射,或者为你的窗口分配一个新的颜色映射。在后一种情况下,每次鼠标移动到你的窗口上时,你窗口 的颜色映射都将替换缺省的屏幕映射,然后你就会看到屏幕花一下其它的窗口颜色也改变了。实际上,这和你在使用“-install”选项运行X应用时效果一 样。
系统定义了宏DefaultColormap来获取屏幕的标准颜色映射:
Colormap screen_colormap = DefaultColormap(display, DefaultScreen(display));
上面的调用将会返回第一个屏幕的缺省颜色映射的句柄(再多余的提醒一下,一个X服务器可以同时支持数个不同的屏幕,每个屏幕都可以有自己的资源)。
另一个选项,分配一个颜色映射,像下面这样工作:
/* first, find the default visual for our screen. */
Visual* default_visual = DefaultVisual(display, DefaultScreen(display));
/* this creates a new color map. the number of color entries in this map */
/* is determined by the number of colors supported on the given screen.  */
Colormap my_colormap = XCreateColormap(display, win, default_visual, AllocNone);
注意,window参数是用来只允许X服务器为指定屏幕分配颜色映射。我们接下来可以使用分配来的颜色映射给同一个屏幕里的任意一个窗口使用。
分配和释放颜色节点
一旦我们获得了颜色映射的访问,我们就可以开始分配颜色。使用函数XAllocNameColor()和XAllocClor()来完成这个工作。首先函 数 XAllocNameColor()获得颜色的名字(例如"红","蓝","棕"等等)然后获得能使用的实际相近颜色。函数XAllocColor()访 问RGB颜色。两个函数都使用XColor结构,它有以下的一些数据域:
unsigned long pixel
颜色映射节点的索引。
unsigned short red
RGB颜色值的红色部分。
unsigned short green
RGB颜色值的绿色部分。
unsigned short blue
RGB颜色值的蓝色部分。
下面是使用的例子:
/* this structure will store the color data actually allocated for us. */
XColor system_color_1, system_color_2;
/* this structure will store the exact RGB values of the named color.  */
/* it might be different from what was actually allocated.             */
XColor exact_color;
/* allocate a "red" color map entry. */
Status rc = XAllocNamedColor(display, screen_colormap, "red", &system_color_1,                             &exact_color);
/* make sure the allocation succeeded. */
if (rc == 0) {
fprintf(stderr, "XAllocNamedColor - allocation of ''red'' color failed.\n");}
else {
printf("Color entry for ''red'' - allocated as (%d,%d,%d) in RGB values.\n",                   system_color_1.red, system_color_1.green, system_color_1.blue);}
/* allocate a color with values (30000, 10000, 0) in RGB. */
system_color_2.red = 30000;
system_color_2.green = 10000;
system_color_2.blue = 0;
Status rc = XAllocColor(display, screen_colormap, &system_color_2);
/* make sure the allocation succeeded. */
if (rc == 0) {
fprintf(stderr, "XAllocColor - allocation of (30000,10000,0) color failed.\n");}
else {
/* do something with the allocated color... */    .    .}
使用一个颜色绘制
我们在分配了希望的颜色之后,我们可以使用它们绘制文本或图形。为了达到目的,我们需要把获得的颜色设置给一些GC(图形上下文)作为前景色和背景色,然 后使用设置好的GC来进行绘制。使用函数XSetForeground()和XSetBackground()来进行,如下:
/* use the previously defined colors as the foreground and background */
/* colors for drawing using the given GC. assume my_gc is a handle to */
/* a previously allocated GC.                                         */
XSetForeground(display, my_gc, screen_color_1.pixel);
XSetForeground(display, my_gc, screen_color_2.pixel);
如你所见,这个是个使用的例子。实际的绘制工作使用我们以前介绍的绘图函数。注意,为了使用各种颜色完成绘制工作,我们可以使用两种方法。我们可以在调用 绘图函数前改变GC的值,也可以使用代表不同颜色的GC。由你自己根据情况使用哪种方法。注意,使用多个GC降消耗X服务器额外的资源,但这样可以使你的 代码显的更紧凑。
作为使用颜色绘制的例子,请察看例子程序color-drawing.c 。这是程序simple-drawing.c 的一个拷贝,我们只是添加了颜色的部分在里面。
X Bitmaps和Pixmaps
一个被称为多媒体的程序所有作的一件事情就是显示图片。在X的世界里,使用bitmaps和pixmaps来实现这个功能。在为我们的程序设置图标的介绍里我们已经使用了它们。现在让我们进一步对它们进行研究,看看在一个窗口里是如何绘制它们的。
在进入之前有一点需要注意,Xlib不能处理许多现在流行的图片格式,例如gif,jpeg或tiff。这些被留给了应用程序(或者是一些图形处理库)来转换成X服务器可以接受的x bitmaps和x pixmaps。
什么是一个X Bitmap?和X Pixmap?
一个Xbitmap是一个有X窗口系统定义的双色图形格式。在保存在一个文件里的时候,bitmap数据看起来就像一段C源程序。它包括定义图片宽高的变量,一个包含比特值得矩阵(矩阵的尺寸=宽*高),和一个可选的热点位置(将会在后面的鼠标光标的部分进行解释)。
一个X pixmap是一个X窗口系统在内存里保存图像的格式。该格式可以储存黑色和白色的图片(例如X bitmaps)也可以保存带颜色的图片。这实际上是X协议唯一能支持的图片格式,任何图片如果想被显示在屏幕上前都要先被转换成这种格式。
实际上,一个X pixmap应该被认为是一个没有被绘制到屏幕上的窗口。许多在窗口上的图形操作也可以工作于X pixmap,只不过使用X pixmap ID来代替窗口ID。事实上,如果你查看手册,你会发现所有的这些函数都是接受一个叫"可画"参数而不是一个窗口参数。因为这两种类型都是可画的,它们都 可以被用在例如函数XDrawArc(),XDrawText()等等。
从一个文件里读取一个Bitmap
在图标的程序里,我们已经看过如何从一个文件里把一个Bitmap装载到内存里。前面我们使用的方法是使用C预编译器"#include"来进行,下面我们看看如何直接从文件里读取。
/* this variable will contain the ID of the newly created pixmap.    */
Pixmap bitmap;
/* these variables will contain the dimensions of the loaded bitmap. */
unsigned int bitmap_width, bitmap_height;
/* these variables will contain the location of the hot-spot of the  */
/* loaded bitmap.                                                    */
int hotspot_x, hotspot_y;
/* this variable will contain the ID of the root window of the screen */
/* for which we want the pixmap to be created.                        */
Window root_win = DefaultRootWindow(display);
/* load the bitmap found in the file "icon.bmp", create a pixmap      */
/* containing its data in the server, and put its ID in the ''bitmap'' */
/* variable.                                                          */
int rc = XReadBitmapFile(display, root_win, "icon.bmp", &bitmap_width,
&bitmap_height, &bitmap, &hotspot_x, &hotspot_y);
/* check for failure or success. */
switch (rc) {
case BitmapOpenFailed:
fprintf(stderr, "XReadBitmapFile - could not open file ''icon.bmp''.\n");
break;
case BitmapFileInvalid:
fprintf(stderr,
"XReadBitmapFile - file ''%s'' doesn''t contain a valid bitmap.\n",                                  "icon.bmp");
break;
case BitmapNoMemory:
fprintf(stderr, "XReadBitmapFile - not enough memory.\n");
break;
case BitmapSuccess:
/* bitmap loaded successfully - do something with it... */
break;
}
注意对于给定的bitmap参数"root_win"什么作用也不起 - 读取的bitmap并不与这个窗口相联系。这个窗口句柄只是被用来指明bitmap所使用的屏幕。这是非常重要的,bitmap必须支持与屏幕相同数量的颜色,这样它才能发挥作用。
在一个窗口里绘制图形
一旦我们获得了从bitmap里生成的pixmap的句柄,我们就可以使用函数XCopyPlane()把它绘制到窗口里。这个函数可以帮助我们指定什么(一个窗口,甚至另一个pixmap)可以画到这个pixmap上去。
/* draw the previously loaded bitmap on the given window, at location   */
/* ''x=100, y=50'' in that window. we want to copy the whole bitmap, so */
/* we specify location ''x=0, y=0'' of the bitmap to start the copy from, */
/* and the full size of the bitmap, to specify how much of it to copy.  */
XCopyPlane(display, bitmap, win, gc, 0, 0, bitmap_width, bitmap_height,
100, 50, 1);
如你所见,我们可以只拷贝制定的矩形区而不是整个pixmap。另外还需要注意的是函数XCopyPlane的最后一个参数(那个结尾的"1")。该参数 指定了那个平面被从源里拷贝出来。对于bitmaps,我们通常只拷贝平面1。到了下面我们讨论颜色深度的时候你就能确切的明白为什么这么做。
创建一个Pixmap
有时我们需要创建一个没有初始化的pixmap,这样我们可以接下来在它上面绘制。这对图像绘制程序是非常有用的。另外,这对读取各种格式的图像数据也是非常有用的。
/* this variable will store the handle of the newly created pixmap. */
Pixmap pixmap;
/* this variable will contain the ID of the root window of the screen */
/* for which we want the pixmap to be created.                        */
Window root_win = DefaultRootWindow(display);
/* this variable will contain the color depth of the pixmap to create. */
/* this ''depth'' specifies the number of bits used to represent a color */
/* index in the color map. the number of colors is 2 to the power of */
/* this depth.                                                       */
int depth = DefaultDepth(display, DefaultScreen(display));
/* create a new pixmap, with a width of 30 pixels, and height of 40 pixels. */
pixmap = XCreatePixmap(display, root_win, 30, 40, depth);
/* just for fun, draw a pixel in the middle of this pixmap. */
XDrawPoint(display, pixmap, gc, 15, 20);
在一个窗口里绘制一个Pixmap
我们在获得了一个pixmap的句柄后,我们就可以使用它在窗口里绘制,使用函数XCopyArea()。
/* draw the previously loaded bitmap on the given window, at location   */
/* ''x=100, y=50'' in that window. we want to copy the whole bitmap, so */
/* we specify location ''x=0, y=0'' of the bitmap to start the copy from, */
/* and the full size of the bitmap, to specify how much of it to copy. */
XCopyPlane(display, bitmap, win, gc, 0, 0, bitmap_width, bitmap_height,                     100, 50, 1);
如你所见,我们可以拷贝指定的矩形区域而不是整个pixmap。
另外一个需要被强调注意的是 - 可以在同一个屏幕上创建不同深度的pixmap。当我们进行拷贝作业时(往一个窗口上拷贝pixmap等等),我们应该保证源和目标是用相同的深度。如果 两个的深度不一样,操作将会失败。除非我们使用前面介绍的函数XCopyPlane()可以完成这个操作。在那样一种情况下,我们拷贝指定的平面到窗口上 去,实际上指定了每一个被拷贝的颜色位。这个操作可以制作许多特殊的效果,但这超出了本文的范围。
释放一个Pixmap
最后,当我们完成了对一个pixmap的操作,我们应该释放它所占的资源。使用函数XFreePixmap()。
/* free the pixmap with the given ID. */
XFreePixmap(display, pixmap);
在释放一个pixmap之后 - 我们绝对不能再访问它。
作为总结这一章,看一下程序draw-pixmap.c 。
改变鼠标光标
我们经常看到改变鼠标光标的程序(经常被称为X光标)。例如,一个正在埋头工作的程序经常会把光标变成沙漏,提示用户需要等待才能处理新的请求。如果没有这么个提示方法,用户会认为程序已经卡住了。下面让我们看看如何为我们的窗口改变鼠标光标。
创建和销毁鼠标光标
系统提供了两个方法来创建光标。其中一个是使用系统预定义的形状,由Xlib支持。另一个是有程序提供一个bitmap来显示。
使用前一种方法时,我们使用一个特殊的字体名字"cursor"和相应的函数XCreateFontCursor()。该函数接受一个形状指示然后返回一个代表生成的光标的句柄。文件
列出了系统支持的光标类型,下面的是其中的三个:
XC_arrow
X服务器显示的普通光标。
XC_pencil
一个笔状的光标。
XC_watch
一个表状沙漏
使用这些符号来创建光标是非常简单的:
#include
/* defines XC_watch, etc. */
/* this variable will hold the handle of the newly created cursor. */
Cursor watch_cursor;
/* create a sand watch cursor. */
watch_cursor = XCreateFontCursor(display, XC_watch);
另一种创建鼠标光标的方法时使用一对pixmaps。一个pixmap定义了光标的形状,另一个是个面具,来指定前一个的什么内容被显示。其它的内容将变 成透明的。创建一个那样的光标使用函数XCreatePixmapCursor()。下面的例子里,我们将使用"icon.bmp"来创建光标。我们将假 设它已经被装载到内存里去了,并已经被转换成pixmap,返回的句柄被保存到"bitmap"变量里。我们希望它是完全透明的。也就是说,只有黑色颜色 的部分会被确实画在屏幕上。为了实现这个效果,我们将会既用它来做光标pixmap且做面具pixmap。希望你能明白为什么这样...
/* this variable will hold the handle of the newly created cursor. *
/Cursor icon_cursor;
/* first, we need to define foreground and background colors for the cursor. */
XColor cursor_fg, cursor_bg;
/* access the default color map of our screen. */
Colormap screen_colormap = DefaultColormap(display, DefaultScreen(display));
/* allocate black and while colors. */
Status rc = XAllocNamedColor(display, screen_colormap, "black", &cursor_fg,                                          &cursor_fg);
if (rc == 0) {
fprintf(stderr,            "XAllocNamedColor - cannot allocate ''black'' ??!!??\n");
exit(1);}
Status rc = XAllocNamedColor(display, screen_colormap, "white",                             &cursor_bg, &cursor_bg);
if (rc == 0) {
fprintf(stderr, "XAllocNamedColor - cannot allocate ''white'' ??!!??\n");
exit(1);}
/* finally, generate the cursor. make the ''hot spot'' be close to the */
/* top-left corner of the cursor - location (x=5, y=4). */
icon_cursor = XCreatePixmapCursor(display, bitmap, bitmap,                  &cursor_fg, &cursor_bg, 5, 4);
上面需要说明的是参数"hot spot"。当我们定义了一个光标,我们需要指明光标里的哪一个像素用来生成各种鼠标事件。通常,我们根据习惯来指定一个看起来像"hot spot"的点。例如一个箭头形状的光标,我们就会选择箭头尖为"hot spot"。
最后,我们不需要再使用光标时,我们可以使用函数XFreeCursor()来释放它的资源:
XFreeCursor(display, icon_cursor);
设置一个窗口的鼠标光标
我们在创建了光标后,就可以告诉X服务器把它贴到我们的任何窗口上去。使用函数XDefineCursor(),X服务器就会在每一次光标移进指定的窗口 时改变光标的形状。稍后我们可以使用函数XUndefinCursor()来撤销刚才的指定。这样,鼠标再移进指定的窗口时就会使用缺省的光标。
/* attach the icon cursor to our window. */
XDefineCursor(display, win, icon_cursor);
/* detach the icon cursor from our window. */
XUndefineCursor(display, win);
作为例子,请查看程序cursor.c, and see
how mouse cursors are set, changed and removed. Run the program, place the
mouse pointer over the created window, and watch.

用51单片机简单做了个无线测温装置

主要设备和实现的功能:

两个51单片机开发板,和两个NRF905无线模块,一个开发板作为从机,利用机上的ds18测温度,然后通过NRF905发送给主机,主机通过NRF905接收从机温度信息,在12864液晶屏上显示的同时,通过串口将数据发送个pc。

效果如图:

 

 

linux下用C编写的基于smtp的邮件发送程序

我的webf服务器是动态ip,因此需要检测服务器ip的变化然后更新域名的ip,打算检测到ip变化后自动发送邮件告知自己,于是上网搜了下编程发送邮件的方法,大部分是基于vc的,我找了个例子修改成了linux下的程序。这个程序是基于smtp协议的,smtp是基于tcp的,有兴趣的可以先自己看看smtp协议,这样看程序会更容易理解。

这个程序目前126的邮箱经测试成功发送,新浪的虽然显示发送成功但是总也收不到,有空再慢慢实验吧,其他邮箱还未测试。

程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <limits.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <ctype.h>

// Function prototype
void StreamClient(char *szServer, short nPort, char *szMessage);
// Helper macro for displaying errors

////////////////////////////////////////////////////////////

int main(int argc, char **argv)
{
char hostname[255];
int nRet;
short nPort;
nPort =25;
StreamClient("smtp.126.com", nPort, "AUTH LOGIN\r\n");
return 0;
}

////////////////////////////////////////////////////////////

void StreamClient(char *szServer, short nPort, char *szMessage)
{

int sockfd;
char buffer[1024];
struct sockaddr_in server_addr;
struct hostent *host;

if((host=gethostbyname(szServer))==NULL)/*取得主机IP地址*/
{
fprintf(stderr,"Gethostname error, %s\n", strerror(errno));
exit(1);
}

if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)/*建立SOCKET连接*/
{
fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
exit(1);
}

/* 客户程序填充服务端的资料 */
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(nPort);
server_addr.sin_addr=*((struct in_addr *)host->h_addr);

/* 客户程序发起连接请求 */
if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)/*连接网站*/
{
fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
exit(1);
}

printf("\nStream Client connecting to server: %s on port: %d",szServer, nPort);

char szBuf[1024];
memset(szBuf,0,1024);
int nRet;
// strcpy(szBuf, "From the Client");
char buf[350]="0";
char server[250];
gethostname(server,250);
strcpy(buf, "HELO ");

strcat(buf, szServer);
strcat(buf, " \r\n");
printf("%s:::%d",buf,strlen(buf));

//
// Wait for a reply
//
nRet = recv(sockfd,szBuf,sizeof(szBuf)+1,0);  printf("\nData received OVER DATA: %s", szBuf);

nRet = send(sockfd, buf, strlen(buf), 0);printf("\nsend %s",buf);
nRet = recv(sockfd, szBuf, sizeof(szBuf), 0); printf("\nData received2: %s", szBuf);

//发送准备登陆信息
nRet = send(sockfd, "AUTH LOGIN\r\n", strlen("AUTH LOGIN\r\n"), 0);
nRet = recv(sockfd, szBuf, sizeof(szBuf), 0); printf("\nData received LOGIN: %s", szBuf);

//发送用户名和密码,这里的用户名和密码必须用base64进行转码,发送转码以后的字符串,对于126邮箱来说用户名是@前面的字符串
nRet = send(sockfd,"转码后的用户名\r\n", strlen("转码后的用户名\r\n"),0);printf("\nData Send USERNAME");
nRet = recv(sockfd, szBuf, sizeof(szBuf),0);printf("\nData received USERNAME: %s", szBuf);printf("\nData Send PASS");

//发送用户密码
nRet = send(sockfd,"转码后的密码\r\n", strlen("转码后的密码\r\n"), 0);
//printf("\nData Send PASS");
nRet = recv(sockfd, szBuf, sizeof(szBuf),0); printf("\nData received USERPASSWORD: %s", szBuf);

//发送[发送邮件]的信箱(改成你的邮箱!),该邮箱要与用户名一致,否则发送不成功
send(sockfd,"MAIL FROM: <server_ip_alert@126.com>\r\n",strlen("MAIL FROM: <server_ip_alert@126.com>\r\n"),0);printf("\nsend MAIL FROM\n");
nRet = recv(sockfd, szBuf, sizeof(szBuf), 0);printf("\nData received MAILFROM: %s", szBuf);

//发送[接收邮件]的邮箱
nRet= send(sockfd,"RCPT TO: <tootoogo@hotmail.com>\r\n",strlen("RCPT TO: <tootoogo@hotmail.com>\r\n"),0);printf("\nsend RCPT TO\r\n");
nRet = recv(sockfd, szBuf, sizeof(szBuf), 0);   printf("\nData received TO MAIL: %s", szBuf);
/*
nRet= send(sockfd,"RCPT TO: <server_ip_alert@hotmail.com>\r\n",strlen("RCPT TO: <server_ip_alert@hotmail.com>\r\n"),0);printf("\nsend RCPT TO\r\n");
nRet = recv(sockfd, szBuf, sizeof(szBuf), 0);   printf("\nData received TO MAIL: %s", szBuf);
*/
char MailData[] ="From: \"server_ip_alert@sina.com\"<server_ip_alert@sina.com>\r\nTo: tootoogo@hotmail.com\r\nSubject: IP Address\r\n\r\n"; //主题可以改成别的

//各诉邮件服务器,准备发送邮件内容

send(sockfd,"DATA\r\n", strlen("DATA\r\n"),0);printf("\nsend DATA\n");
//  nRet = recv(sockfd, szBuf, sizeof(szBuf)+1, 0); printf("\nData receivedSEND DATA: %s", szBuf);
//发送邮件标题
send(sockfd,MailData, strlen(MailData),0);
//发送邮件内容
send(sockfd,"test!!!!\r\n", strlen("abcdefg\r\n"),0);//我随便写了几个字符进行测试
//发送邮件结束
send(sockfd,"\r\n.\r\n", strlen("\r\n.\r\n"),0);
//接收邮件服务器返回信息
nRet = recv(sockfd,szBuf,sizeof(szBuf),0);  printf("\nData received OVER DATA: %s", szBuf);
send(sockfd,"QUIT\r\n", strlen("QUIT\r\n"),0);
nRet = recv(sockfd,szBuf,sizeof(szBuf),0);  printf("\nData received QUIT: %s", szBuf);

//
// Display the received data
//
//printf("\nData received3: %s", szBuf);

close(sockfd);
return;
}

转载:opencv2.1的arm移植

OpenCV是Intel支持的开源计算机视觉库。它由一系列C函数和少量C++类构成,实现了图像处理和计算机视觉方面的很多通用算法。它不依赖于其它的外部库—尽管也可以使用某些外部库。

 

OpenCV使用BSD License,对非商业应用和商业应用都可以免费使用。

 

OpenCV的主要应用环境是Windows和Linux,对于嵌入式系统如arm-linux,很少有资料提到,因此将我在arm-linux编译过程记录下来和大家分享。
预备工作:

 

a. 安装交叉编译链
我使用的是arm-linux-gcc 4.3.2,解压到目录/usr/local/arm/4.3.2,然后在环境变量PATH中增加/usr/local/arm/4.3.2/bin

b. 安装CMake
OpenCV 2.0之后的版本,必须使用CMake创建Makefile。我使用的CMake版本是2.8.2,解压到目录/usr/local/cmake- 2.8.2-Linux-i386,然后在环境变量PATH中增加/usr/local/cmake-2.8.2-Linux-i386/bin
编译OpenCV:


1、解压OpenCV 2.1到/usr/local/OpenCV-2.1.0目录下

 

2、创建/usr/local/opencv-arm/目录,作为CMake编译arm版本的工作目录

 

3、在X环境下,运行cmake-gui
选择源代码目录:/usr/local/OpenCV-2.1.0
选择Build目录:/usr/local/opencv-arm/
点击Configure,保持generator为Unix Makefiles,选择Specify options for cross-compiling,点击Next
Operating System填写arm-inux
C Compilers填写/usr/local/arm/4.3.2/bin/arm-linux-gcc
C++ Compilers填写/usr/local/arm/4.3.2/bin/arm-linux-g++
程序库的Target Root填写/usr/local/arm/4.3.2/,然后点击Finish

修改默认配置,默认安装目录为/usr/local,对于交叉编译的库来说并不合适,所以我把CMAKE_INSTALL_PREFIX变量改为/usr/local/arm/lib/opencv/
另外,我没有安装tiff图像的支持,因此去掉WITH_TIFF

点击Generate生成Makefile

 

4、在终端界面中,进入目录/usr/local/opencv-arm,运行make编译opencv

 

编译时发现如下错误:
Linking CXX executable ../../bin/opencv_createsamples
../../lib/libcxcore.so: undefined reference to `clock_gettime'
../../lib/libcxcore.so: undefined reference to `pthread_key_create'
../../lib/libcxcore.so: undefined reference to `pthread_getspecific'
../../lib/libcxcore.so: undefined reference to `pthread_setspecific'

原因是cmake不认识我定义的arm-linux系统标记,没有加上库pthread和rt的链接选项

 

5、修改CMakeCache.txt,CMAKE_EXE_LINKER_FLAGS原来为空,加上-lpthread -lrt,重新编译,错误消除

 

6、运行make install,将opencv生成的库和头文件安装到目录/usr/local/arm/lib/opencv/

 

测试OpenCV库

 

1、首先确认一下库是否已编译正确及其安装位置

 

查看头文件:

 

    1. [root@localhost opencv-arm]# ls /usr/local/arm/lib/opencv/include/opencv/
    2. cvaux.h    cvcompat.h  cv.hpp        cvtypes.h  cvvidsurv.hpp  cxcore.h    cxerror.h  cxmat.hpp  cxoperations.hpp  highgui.h    ml.h
    3. cvaux.hpp  cv.h        cvinternal.h  cvver.h    cvwimage.h     cxcore.hpp  cxflann.h  cxmisc.h   cxtypes.h         highgui.hpp

 

 

查看库文件:

  1. [root@localhost opencv-arm]# ls /usr/local/arm/lib/opencv/lib
  2. libcv.a  libcvaux.a  libcvaux.so  libcv.so  libcxcore.a  libcxcore.so  libhighgui.a  libhighgui.so  libml.a  libml.so2、 写个简单的测试程序,打开摄像头并创建一个窗口显示 
      1. // test.cpp
      2. #include <cv.h>
      3. #include <cxcore.h>
      4. #include <highgui.h>
      5. int main()
      6. {
      7.         CvCapture* capture = NULL;
      8.         IplImage* frame = NULL;
      9.         if( !(capture = cvCaptureFromCAM(-1)))
      10.         {
      11.                 fprintf(stderr, "Can not open camera./n");
      12.                 return -1;
      13.         }
      14.         cvNamedWindow("video", 1);
      15.         while(frame = cvQueryFrame( capture ) )
      16.         {
      17.                 cvShowImage("video", frame);
      18.         }
      19.         cvDestroyWindow("video");
      20.         cvReleaseCapture(&capture);
      21.         return 0;
      22. }

    #include <cv.h>
    #include <cxcore.h>
    #include <highgui.h>

    int main()
    {
    CvCapture* capture = NULL;
    IplImage* frame = NULL;

    if( !(capture = cvCaptureFromCAM(-1)))
    {
    fprintf(stderr, "Can not open camera./n");
    return -1;
    }

    cvNamedWindow("video", 1);

    while(frame = cvQueryFrame( capture ) )
    {
    cvShowImage("video", frame);
    }

    cvDestroyWindow("video");
    cvReleaseCapture(&capture);
    return 0;
    }

    3、编译链接测试程序

     

    arm-linux-g++ -I/usr/local/arm/lib/opencv/include/opencv/ -L/usr/local/arm/lib/opencv/lib/ -lcv -lcxcore -lhighgui -lpthread -lrt -o test test.cpp

     

    4、复制程序到嵌入式系统中运行

     

    首先复制主机/usr /local/arm/lib/opencv/lib/下面的几个.so文件到嵌入式Linux系统的/lib/目录下,再复 制我们编译的test到嵌入式系统/opt/myworks/目录下(并确保文件test属性为可执行),如果test可正常运行没有报告缺少库文件,说 明我们编译的arm-linux版OpenCV库已经可以正常使用。

Opencv内存jpg图像解码和编码

找到两个函数:
CV_IMPL CvMat* cvEncodeImage( const char* ext,const CvArr* arr, const int* _params )

CV_IMPL IplImage* cvDecodeImage( const CvMat* _buf, int iscolor )
据说可以编码/解码bmp, png, ppm and tiff (encoding only).

解码测试例子:

#include <cv.h>

#include <stdio.h>

main()

{

FILE *f=fopen("a.jpg","rb");//随便读一个jpg文件
unsigned char *m=(unsigned char*)malloc(1267*791);//分配内存足够大,放得下所读取的jpg文件
fread(m,50000,1,f);//读取文件

CvMat mat = cvMat(1267,791,CV_8UC1, m);//这里矩阵的行列随便定义,但是矩阵行列乘积要足够大,要能装下解压后的图片文件,jpg文件解压后会是原文件的N多倍,建议使用jpg文件的宽高作为矩阵的行列。最好事先知道所读取得图片分辨率,不然矩阵太小了,解压会失败。
IplImage *p = cvDecodeImage( &mat, 1 );//解码
cvNamedWindow("a");

cvShowImage("a",p);
cvWaitKey(0);

cvReleaseImage(&p)

free(m);

}

在Archlinux 4.4.3-1下测试成功,编译使用gcc `pkg-config opencv --cflags --libs` test.c -o test

原创:dyndns使用经验(Arclinux or Freebsd下)

原因

以前使用www.3322.org的二级域名,但是3322的客户端更新ip的时候得到的ip不对,每次都是手动更新,所以转到www.dyndns.org的二级域名了。

//////////////////////////////

附:ddclient配置和使用方法

ddclient的Readme很简单,使用的时候把ddclient拷贝到/usr/sbin,然后在/etc/ddclient/ddclient.conf中加入

## ddclient configuration file
daemon=60
# check every 600 seconds
syslog=yes
# log update msgs to syslog
mail-failure=xxx@xxxl.com # Mail failed updates to user
#pid=/var/run/ddclient.pid
# record PID in file.
## Detect IP with our CheckIP server
use=web, web=checkip.dyndns.com/, web-skip='IP Address'
## DynDNS username and password here
login=username
password=yourpasswd
## Default options
protocol=dyndns2
server=members.dyndns.org
## Dynamic DNS hosts
your_donmain_name.dyndns.org

注意ddclient.conf的权限和所有人,不然ddclient会报错,然后手动运行ddclient就可以了,可以在/var/log/message里面看到ddclient的更新信息。

/////////////////////////////

dyndns的客户端ddclient更新ip效果很好,但是我的是动态ip,设置ddclient定时更新后发现,我的二级域名被dyndns给block了,原来,dyndns网站只要发现ip地址没有发生变化的情况下进行多次更新就会把你的二级域名给block。

我的想法

于是,我想如果做个小程序判断自己的ip是否发生变化,如果变化了在用ddclient更新,就不会被dyndns给block了。检测ip变化可以利用dyndns提供的网页ip检测,即访问网址http://checkip.dyndns.org就可以得ip了,花了个把小时把别人的一个http下载程序修改了下,基本满足了我的需要。该程序每隔20分钟检测下ip是否变化,变化了就用ddclient更新。

源程序如下:

//access http://checkip.dyndns.org/,then return your ip address

//程序采用非阻塞模式
/******* http客户端程序 httpclient.c ************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <limits.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <ctype.h>
//#include <stropts.h>
#include <unistd.h>
#include <fcntl.h>

//////////////////////////////httpclient.c 开始///////////////////////////////////////////

/********************************************
功能:搜索字符串右边起的第一个匹配字符
********************************************/
char * Rstrchr(char * s, char x)  {

if(!(*s))  return 0;
int i = strlen(s);
while(s[i-1]) if(strchr(s + (i - 1), x))  return (s + (i - 1));  else i--;
return 0;
}

/********************************************
功能:把字符串转换为全小写
上述代码中的:while(*s) *s=tolower(*s++);
应该改为:wh
********************************************/
void ToLowerCase(char * s)  {

while(s && *s) {*s=tolower(*s);s++;}
}

/**************************************************************
功能:从字符串src中分析出网站地址和端口,并得到用户要下载的文件
***************************************************************/
void GetHost(char * src, char * web, char * file, int * port)  {
char * pA;
char * pB;
memset(web, 0, sizeof(web));
memset(file, 0, sizeof(file));
*port = 0;
if(!(*src))  return;
pA = src;
if(!strncmp(pA, "http://", strlen("http://")))  pA = src+strlen("http://");
else if(!strncmp(pA, "https://", strlen("https://")))  pA = src+strlen("https://");
pB = strchr(pA, '/');
if(pB)  {
memcpy(web, pA, strlen(pA) - strlen(pB));
if(pB+1)  {
memcpy(file, pB + 1, strlen(pB) - 1);
file[strlen(pB) - 1] = 0;
}
}
else  memcpy(web, pA, strlen(pA));
if(pB)  web[strlen(pA) - strlen(pB)] = 0;
else  web[strlen(pA)] = 0;
pA = strchr(web, ':');
if(pA)  *port = atoi(pA + 1);
else *port = 80;
}
////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
int sockfd;
char buffer[1024],buffer_bak[1024],buffer_tmp[1024];
struct sockaddr_in server_addr;
struct hostent *host;
int portnumber,nbytes;
char host_addr[256];
char host_file[1024];
char request[1024];
char pattern[]="<html><head><title>Current IP Check</title></head><body>Current IP Address";
int send, totalsend;
int k,i;
long flag=0,*longp;
char * pt;
char dyndns_site[]="http://checkip.dyndns.org/";

//this send http request to this web site will get your ip address
ToLowerCase(dyndns_site);/*将参数转换为全小写*/
//  printf("lowercase parameter.1 is: %s\n", dyndns_site);
GetHost(dyndns_site, host_addr, host_file, &portnumber);/*分析网址、端口、文件名等*/
memset(buffer_bak, 0, sizeof(buffer_bak));
memset(buffer, 0, sizeof(buffer));
memset(buffer_tmp, 0, sizeof(buffer_tmp));
sockfd=0;
while(1)
{
if((host=gethostbyname(host_addr))==NULL)/*取得主机IP地址*/
{
sleep(10);
printf("myddclient:network error 1,can not resolv host\n");
continue;
}
/* 客户程序开始建立 sockfd描述符 */
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)/*建立SOCKET连接*/
{
sleep(10);
printf("myddclient:network error 2,can not initial socket\n");
continue;
}
/* 客户程序填充服务端的资料 */
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(portnumber);
server_addr.sin_addr=*((struct in_addr *)host->h_addr);
/* 客户程序发起连接请求 */
if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)/*连接网站*/
{
sleep(10);
printf("myddclient:network error 3,can not connect to host\n");
close(sockfd);
continue;
}

//set to nonblock mode
fcntl(sockfd, F_SETFL, O_NDELAY);
sprintf(request, "GET /%s HTTP/1.1\r\nAccept: */*\r\nAccept-Language: zh-cn\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\r\nHost: %s:%d\r\nConnection: Close\r\n\r\n", host_file, host_addr, portnumber);
/*取得真实的文件名*/
if(host_file && *host_file)  pt = Rstrchr(host_file, '/');
else pt = 0;
/*发送http请求request*/
send = 0;totalsend = 0;
nbytes=strlen(request);
k=0;
while(totalsend < nbytes) {
send = write(sockfd, request + totalsend, nbytes - totalsend);
if(send==-1)  {printf("myddclient:send error!%s\n", strerror(errno));k=1;   close(sockfd); break;}
totalsend+=send;
}
if(k==1)continue;

/* 连接成功了,接收http响应,response */
k=0;
i=0;
sleep(20);
memset(buffer_tmp, 0, sizeof(buffer_tmp));
nbytes=read(sockfd,buffer,1024);
flag=-1;
while(nbytes>0)
{
nbytes--;
flag++;
if(i < 4)  {
if(buffer[flag] == '\r' || buffer[flag] == '\n')  i++;
else i = 0;
}
else  {
buffer_tmp[k]=buffer[flag];
k++;if(k==1024)k=1023;
i++;
if(i==1024)i=1023;
}
}
k=0;
for(i=0;i<strlen(pattern);i++)
if(buffer_tmp[i]!=pattern[i])
{
k=1;
break;
}
if(strcmp(buffer_tmp,buffer_bak)!=0&&k==0)
{
system("/usr/sbin/ddclient");
printf("myddclient ok!\n");
}
else
{
if(k==0)
{
// printf("need not update!\n");
}
else
{
printf("myddclient:error\n get html page is %s!\n",buffer_tmp);
// flag++;
//longp=(long*)buffer_bak;
//*longp=flag;
}
}
memset(buffer_bak, 0, sizeof(buffer_bak));
strcpy(buffer_bak,buffer_tmp);
memset(buffer_tmp,0,sizeof(buffer_tmp));
close(sockfd);
sockfd=0;
sleep(1200);
/* 结束通讯 */

}
}
//////////////////////////////httpclient.c 结束///////////////////////////////////////////

//////////////////////////////httpclient.c 结束///////////////////////////////////////////

Archlinux下拍摄视频和照片的程序

买了个华硕EeePC,装了archlinux,上面有个摄像头,于是利用opencv做了连个程序一个用来拍视频一个用来拍照片

程序一,my_shot.c拍摄照片

#include "opencv/cv.h"
#include <time.h>
//compile it use: gcc `pkg-config opencv --cflags --libs` -o my_shot my_shot.c
#include <stdio.h>
#include "opencv/highgui.h"
void main(int argc, char* argv[])
{
time_t now;
struct tm *ts;
now = time(NULL);
ts   =   localtime(&now);

char filename[512];
// gettimeofday(&ts);
//  sprintf(filename,"%s",ctime(&now));;
sprintf(filename,"Y%d.%d.%d-H%d.%d_%d.jpg",ts->tm_year-100,ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);;
printf("%s\n",filename);
//clock_gettime(CLOCK_REALTIME, &ts);
//    cvNamedWindow("Test",CV_WINDOW_AUTOSIZE);
CvCapture* capture = cvCreateCameraCapture(0);
IplImage *frame=0,*myframe;
frame = cvQueryFrame(capture );
if(!frame){printf("fail to init camera!\n");return;}
//initial myframe
CvSize size;
size.width=frame->width;
size.height=frame->height;
myframe=cvCreateImage( size, IPL_DEPTH_8U, 1);
cvCopy(frame,myframe,NULL);
cvConvertImage(myframe,frame,CV_CVTIMG_FLIP);
if(!frame)return;
cvSaveImage(filename,myframe,0);
cvReleaseCapture( &capture );
cvReleaseImage(&myframe);
}

程序二,my_record.c 拍摄视频

//compile it use: gcc `pkg-config opencv --cflags --libs` -o my_record my_record.c
//this program can serve as a base program for develop complicated program
#include "cv.h"
#include "highgui.h"
#include "cxcore.h"
#include <time.h>
#include <stdio.h>
IplImage* myframe;
CvVideoWriter * pVW=NULL;
CvCapture* capture=0;;
char filename[512];
int left=0,right=0;
CvSize size;
time_t now;
struct tm *ts;
void Mouse(int event, int x, int y, int flags,void*para)
{

if(event==1)
{
if(!right) left=1;
}
if(event==2)
{
switch(right)
{
case 1:
right=0;
cvReleaseVideoWriter(&pVW);
pVW=0;
break;
case 0:
right=1;
now = time(NULL);
ts   =   localtime(&now);
sprintf(filename,"Y%d.%d.%d-H%d.%d_%d.mpg",ts->tm_year-100,ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
//      pVW=cvCreateVideoWriter(filename,CV_FOURECC('p','I','M','l'),25,size,1);
pVW=cvCreateVideoWriter(filename,0,25,size,1);
if(!pVW){printf("fail to init record file!\n");return;}
break;
}

}
//press middle button to save image
if(event==4)
{    //char filename[]="C0.jpg";
//filename[1]=a;
//a++;
//printf("save result in file %s\n",filename);
//my_write_data(char* filename,CvMat*mat)
//cvSaveImage( filename, dst_img );
//            printf("event %d flag %d x %d y %d\n",event,mbox.flag,x,y);

}

}

void main(int argc, char* argv[])
{
//    cvNamedWindow("Test",CV_WINDOW_AUTOSIZE);
cvNamedWindow("Camera",0);
capture= cvCreateCameraCapture(0);
CvFont tFont;
cvInitFont(&tFont,  CV_FONT_HERSHEY_COMPLEX, 0.5f,0.7f,0,1,8);
char c;
IplImage *frame;
cvSetMouseCallback( "Test", Mouse,NULL );
frame = cvQueryFrame(capture );
if(!frame){printf("Init camera error!\n");return;}
//initial myframe

size.width=frame->width;
size.height=frame->height;
myframe=cvCreateImage( size, IPL_DEPTH_8U, 1);
while(1){
frame = cvQueryFrame(capture );
//        cvCvtColor(frame,myframe,CV_RGB2GRAY);
cvConvertImage(myframe,frame,CV_CVTIMG_FLIP);
//    if(!frame)break;
if(left){
left=0;
now = time(NULL);
ts   =   localtime(&now);
sprintf(filename,"Y%d.%d.%d-H%d.%d_%d.jpg",ts->tm_year-100,ts->tm_mon=1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);;
cvSaveImage(filename,myframe,0);
cvNamedWindow("shot",0);
cvPutText(myframe,filename, cvPoint(95,15), &tFont,  CV_RGB(255,0,0) );
cvShowImage("shot",myframe);

}
if(right)
{
cvWriteFrame(pVW,myframe);
cvPutText(myframe, "Recording!", cvPoint(95,15), &tFont,  CV_RGB(255,0,0) );
}
cvShowImage( "camera", myframe );
if(!right)c = cvWaitKey(33);
if( c == 27 ) break;
}
cvReleaseCapture( &capture );
cvReleaseImage(&myframe);
cvDestroyWindow( "camera" );
cvDestroyWindow( "shot" );
if(!pVW) cvReleaseVideoWriter(&pVW);

}

原创:霍夫曼编码实例程序(Huffman)

偶然需要用到Huffman编码,懒得自己编写,上网一搜,有很多代码,下载好几个都不能用,不是太长,就是有错,没办法,自己看看书,自己写一下吧,这个程序花了个把小时自己写的,效率不高但是可以用。该程序在linux下用gcc编译通过。

#include<stdio.h>
#include <stdlib.h>
#define MAXNODE 1000   //最大编码个数
struct Node   //Huffman树节点
{
struct Node *left;
struct Node *right;
struct Node * par;//指向父节点,用于删除节点和生成编码
char data;
int freq;
};
/////////////////////////////////////////////////////////
struct Node * find_min(struct Node **data_node,int*index)

//在data_node数组中找到频度最小的那个节点,index保存该节点在数组中的位置
{
int i=0,d=100000;
struct Node *r;
r=(struct Node*)-1;
while(1)
{
// printf("%d\n",*(data_node+i));
if(( *(data_node+i))!=NULL)
if((*(data_node+i))->freq<=d)
{
d=(*(data_node+i))->freq;
r=*(data_node+i);
*index=i;
}
i++;
if(i>=MAXNODE)break;
}

if((int)r!=-1)
printf("find min freq %d data %c index %d\n",r->freq,r->data,*index);
return r;
}
////////////////////////////////////////////////////////
struct Node * merge(struct Node *t1,struct Node *t2)

//合并两个节点,重新生成一个新节点,其频度为两个节点频度和,新节点左右子树指向这两个节点

{
struct Node *t;
t=(struct Node*)malloc(sizeof(struct Node));
t->freq=t1->freq+t2->freq;
t->data='*';
t->par=NULL;
t1->par=t;
t2->par=t;
if((t1->freq)>=(t2->freq))
{
t->left=t1;
t->right=t2;
}
else
{
t->left=t2;
t->right=t1;
}
printf("merge %c(%d)&%c(%d) to * freq %d \n",t1->data,t1->freq,t2->data,t2->freq,t->freq);
return t;

}
///////////////////////////////////////////////

void show_huffman_code(struct Node **tree)

//显示Huffman编码

{
int i=0;
struct Node *tmp1,*tmp2;
while(*(tree+i)!=NULL)
{
tmp1=(*(tree+i));
tmp2=(*(tree+i))->par;
printf("%c\n",tmp1->data);
while(tmp2)
{
if(tmp2->left==tmp1)
printf("1");
else
if(tmp2->right==tmp1)
printf("0");
tmp1=tmp2;
tmp2=tmp2->par;
}
printf("\n");
i++;

}

}
/////////////////////////
void destroy_tree(struct Node **tree)

//销毁树
{
int i=0,j=0;
struct Node *tmp1,*tmp2;
printf("destroy tree begin\n");
while(*(tree+i)!=NULL)
{
tmp1=(*(tree+i));
tmp2=(*(tree+i))->par;
//   printf("%c\n",tmp1->data);
j++;
printf("destroy %d data %d--",j,tmp1->data);
free(tmp1);
while(tmp2)
{
if(tmp2->left)
tmp2->left->par=NULL;
if(tmp2->right)
tmp2->right->par=NULL;
tmp1=tmp2->par;
j++;
printf("destroy %d data %d--",j,tmp2->data);
free(tmp2);
tmp2=tmp1;
}
i++;
}
printf("\ndestroy tree end\n");
}

/////////////////////////////////////////////////////
main()
{
int i,index;
struct Node *data_node[MAXNODE],*bak_node[MAXNODE];
struct Node *tmpnode1,*tmpnode2;
for(i=0;i <MAXNODE;i++)

{

data_node[i]=NULL;
bak_node[i]=NULL;
}

//测试例

//给7个字符的节点进行赋值,赋值包括节点字符、和频度

for(i=0;i<7;i++)
{
data_node[i]=(struct Node*)malloc(sizeof(struct Node));
data_node[i]->left=NULL;
data_node[i]->right=NULL;
data_node[i]->par=NULL;
bak_node[i]=data_node[i];

}
data_node[0]->data='1';//字符
data_node[0]->freq=200;//频度,表示字符1 在某字符串序列中出现了200次

data_node[1]->data='2';
data_node[1]->freq=190;

data_node[2]->data='3';
data_node[2]->freq=180;

data_node[3]->data='4';
data_node[3]->freq=170;

data_node[4]->data='5';
data_node[4]->freq=150;

data_node[5]->data='6';
data_node[5]->freq=100;

data_node[6]->data='7';
data_node[6]->freq=10;

/////////////////////////////

while(1)
{
tmpnode1=find_min(data_node,&index);
data_node[index]=NULL;
tmpnode2=find_min(data_node,&index);
if((int)tmpnode2==-1)break;
data_node[index]=merge(tmpnode1,tmpnode2);

}

show_huffman_code(bak_node);

destroy_tree(bak_node);
//printf("%d ",tmpnode1->left->left->left);
// data_node[index]=merge(tmpnode1,tmpnode2);
///////////////////////////

}

第 1 页,共 4 页1234