随笔 - 16, 文章 - 0, 评论 - 55, 引用 - 0
数据加载中……

跨平台的鼠标移动源代码

#include < stdio.h >
#include < stdlib.h >
#include < string.h >

#ifdef WIN32
#include < windows.h >
#else
#include < unistd.h >
#include < X11/X.h >
#include < X11/Xlib.h >
#endif

int main(int argc, char* argv[])
{
    char sx[10], sy[10];
    int x, y;

    if ( argc < 3 ) return 0;
    strcpy(sx, argv[1]);
    strcpy(sy, argv[2]);
    x = atoi(sx);
    y = atoi(sy);

#ifdef WIN32
    SetCursorPos(x, y);
#else
    Display *dpy;
    Window rootwindow;

    dpy = XOpenDisplay(NULL);
    if ( ! dpy ) {
        printf("Couldn't open Xdisplay.\n");
        exit(1);
    }
    rootwindow = DefaultRootWindow(dpy);

    XWarpPointer(dpy, rootwindow, rootwindow, 0, 0, 0, 0, x, y);

    XCloseDisplay(dpy);
#endif
    return 0;
}


posted @ 2006-05-31 13:22 cyantree 阅读(656) | 评论 (0)编辑 收藏

跨平台的INI处理源代码

// IniFile.h

#ifndef INIFILE_H
#define INIFILE_H

#include < stdio.h >
#include < stdlib.h >
#include < string.h >

#ifndef OWP_DONT_DEF_FALSE
#ifndef FALSE
#define FALSE 0
#endif
#endif

#ifndef OWP_DONT_DEF_TRUE
#ifndef TRUE
#define TRUE 1
#endif
#endif

#ifndef OWP_DONT_DEF_BOOL
#ifndef BOOL
#define BOOL unsigned int
#endif
#endif

#ifndef OWP_DONT_DEF_CCHR
#ifndef CCHR
#define CCHR const char
#endif
#endif

#ifndef OWP_DONT_DEF_UCHR
#ifndef UCHR
#define UCHR unsigned char
#endif
#endif

#ifndef OWP_DONT_DEF_UCCHR
#ifndef UCCHR
#define UCCHR const unsigned char
#endif
#endif

#ifndef OWP_DONT_DEF_UINT
#ifndef UINT
#define UINT unsigned int
#endif
#endif

#ifndef OWP_DONT_DEF_WORD
#ifndef WORD
#define WORD unsigned short
#endif
#endif

#ifdef LINUX /* Remove CR, on unix systems. */
#define INI_REMOVE_CR
#define DONT_HAVE_STRUPR
#endif

#define tpNULL 0
#define tpSECTION 1
#define tpKEYVALUE 2
#define tpCOMMENT 3

struct ENTRY
{
char Type;
char *pText;
struct ENTRY *pPrev;
struct ENTRY *pNext;
};

typedef struct
{
struct ENTRY *pSec;
struct ENTRY *pKey;
char KeyText [128];
char ValText [128];
char Comment [255];
} EFIND;

/* Macros */
#define ArePtrValid(Sec,Key,Val) ((Sec!=NULL)&&(Key!=NULL)&&(Val!=NULL))

class CIniFile
{
public:
CIniFile (void)
  {
    m_pEntry = NULL;
    m_pCurEntry = NULL;
    m_result [0] = 0;
    m_pIniFile = NULL;
  }

  ~CIniFile (void)
  {
    FreeAllMem ();
  }

BOOL OpenIniFile (CCHR *pFileName)
  {
    char Str [255];
    char *pStr;
    struct ENTRY *pEntry;
    
    FreeAllMem ();
    
    if (pFileName == NULL) { return FALSE; }
    if ((m_pIniFile = fopen (pFileName, "r")) == NULL) { return FALSE; }
    
    while (fgets (Str, 255, m_pIniFile) != NULL) {
      pStr = strchr (Str, '\n');
      if (pStr != NULL) { *pStr = 0; }
      pEntry = MakeNewEntry ();
      if (pEntry == NULL) { return FALSE; }
      
      #ifdef INI_REMOVE_CR
      Len = strlen(Str);
      if ( Len > 0 ) {
        if ( Str[Len-1] == '\r' ) {
          Str[Len-1] = '\0';
        }
      }
      #endif
      
      pEntry->pText = (char *)malloc (strlen (Str)+1);
      if (pEntry->pText == NULL) {
        FreeAllMem ();
        return FALSE;
      }
      strcpy (pEntry->pText, Str);
      pStr = strchr (Str,';');
      if (pStr != NULL) { *pStr = 0; } /* Cut all comments */
      if ( (strstr (Str, "[") > 0) && (strstr (Str, "]") > 0) ) /* Is Section */ {
        pEntry->Type = tpSECTION;
      } else {
        if (strstr (Str, "=") > 0) {
          pEntry->Type = tpKEYVALUE;
        } else {
          pEntry->Type = tpCOMMENT;
        }
      }
      m_pCurEntry = pEntry;
    }
    fclose (m_pIniFile);
    m_pIniFile = NULL;
    return TRUE;
  }

void CloseIniFile ()
  {
    FreeAllMem ();
    if (m_pIniFile != NULL) {
      fclose (m_pIniFile);
      m_pIniFile = NULL;
    }
  }

bool WriteIniFile (CCHR *pFileName)
  {
    struct ENTRY *pEntry = m_pEntry;
    if (m_pIniFile != NULL) {
      fclose (m_pIniFile);
    }
    if ((m_pIniFile = fopen (pFileName, "wb")) == NULL) {
      FreeAllMem ();
      return FALSE;
    }
    
    while (pEntry != NULL) {
      if (pEntry->Type != tpNULL) {
      #ifdef INI_REMOVE_CR
        fprintf (m_pIniFile, "%s\n", pEntry->pText);
      #else
        fprintf (m_pIniFile, "%s\r\n", pEntry->pText);
      #endif
      }
      pEntry = pEntry->pNext;
    }
    
    fclose (m_pIniFile);
    m_pIniFile = NULL;
    return TRUE;
  }
  
  bool  DeleteKey (CCHR *pSection, CCHR *pKey)
  {
    EFIND List;
    struct ENTRY *pPrev;
    struct ENTRY *pNext;
    
    if (FindKey (pSection, pKey, &List) == TRUE) {
      pPrev = List.pKey->pPrev;
      pNext = List.pKey->pNext;
      if (pPrev) {
        pPrev->pNext=pNext;
      }
      if (pNext) {
        pNext->pPrev=pPrev;
      }
      FreeMem (List.pKey->pText);
      FreeMem (List.pKey);
      return TRUE;
    }
    return FALSE;
  }

  BOOL ReadBool (CCHR *pSection, CCHR *pKey, BOOL Default)
  {
    char Val [2] = {"0"};
    if (Default != 0) { Val [0] = '1'; }
    return (atoi (ReadString (pSection, pKey, Val))?1:0); /* Only allow 0 or 1 */
  }

int ReadInt (CCHR *pSection, CCHR *pKey, int Default)
  {
    char Val [12];
    sprintf (Val,"%d", Default);
    return (atoi (ReadString (pSection, pKey, Val)));
  }

double ReadDouble (CCHR *pSection, CCHR *pKey, double Default)
  {
    double Val;
    sprintf (m_result, "%1.10lE", Default);
    sscanf (ReadString (pSection, pKey, m_result), "%lE", &Val);
    return Val;
  }

CCHR *ReadString (CCHR *pSection, CCHR *pKey, CCHR *pDefault)
  {
    EFIND List;
    if (ArePtrValid (pSection, pKey, pDefault) == FALSE) { return pDefault; }
    if (FindKey (pSection, pKey, &List) == TRUE) {
      strcpy (m_result, List.ValText);
      return m_result;
    }
    return pDefault;
  }

void WriteBool (CCHR *pSection, CCHR *pKey, bool Value)
  {
    char Val [2] = {'0',0};
    if (Value != 0) { Val [0] = '1'; }
    WriteString (pSection, pKey, Val);
  }
  
void WriteInt (CCHR *pSection, CCHR *pKey, int Value)
  {
    char Val [12]; /* 32bit maximum + sign + \0 */
    sprintf (Val, "%d", Value);
    WriteString (pSection, pKey, Val);
  }

void WriteDouble (CCHR *pSection, CCHR *pKey, double Value)
  {
    char Val [32]; /* DDDDDDDDDDDDDDD+E308\0 */
    sprintf (Val, "%1.10lE", Value);
    WriteString (pSection, pKey, Val);
  }

void WriteString (CCHR *pSection, CCHR *pKey, CCHR *pValue)
  {
    EFIND List;
    char Str [255];
    
    if (ArePtrValid (pSection, pKey, pValue) == FALSE) { return; }
    if (FindKey (pSection, pKey, &List) == TRUE) {
      sprintf (Str, "%s=%s%s", List.KeyText, pValue, List.Comment);
      FreeMem (List.pKey->pText);
      List.pKey->pText = (char *)malloc (strlen (Str)+1);
      strcpy (List.pKey->pText, Str);
    } else {
      if ((List.pSec != NULL) && (List.pKey == NULL)) /* section exist, Key not */ {
        AddKey (List.pSec, pKey, pValue);
      } else {
        AddSectionAndKey (pSection, pKey, pValue);
      }
    }
  }

protected:
  struct ENTRY *m_pEntry;
  struct ENTRY *m_pCurEntry;
  char m_result [255];
  FILE *m_pIniFile;
  
  void AddKey (struct ENTRY *pEntry, CCHR *pKey, CCHR *pValue)
  {
    char Text [255];
    sprintf (Text, "%s=%s", pKey, pValue);
    AddItemAt (pEntry, tpKEYVALUE, Text);
  }
  
  BOOL AddItem (char Type, CCHR *pText)
  {
    struct ENTRY *pEntry = MakeNewEntry ();
    if (pEntry == NULL) { return FALSE; }
    pEntry->Type = Type;
    pEntry->pText = (char*)malloc (strlen (pText) +1);
    if (pEntry->pText == NULL) {
      free (pEntry);
      return FALSE;
    }
    strcpy (pEntry->pText, pText);
    pEntry->pNext = NULL;
    if (m_pCurEntry != NULL) { m_pCurEntry->pNext = pEntry; }
    m_pCurEntry = pEntry;
    return TRUE;
  }
  
  bool AddItemAt (struct ENTRY *pEntryAt, char Mode, CCHR *pText)
  {
    struct ENTRY *pNewEntry;
    if (pEntryAt == NULL) { return FALSE; }
    pNewEntry = (struct ENTRY*) malloc (sizeof (ENTRY));
    if (pNewEntry == NULL) { return FALSE; }
    pNewEntry->pText = (char *) malloc (strlen (pText)+1);
    if (pNewEntry->pText == NULL) {
      free (pNewEntry);
      return FALSE;
    }
    strcpy (pNewEntry->pText, pText);
    if (pEntryAt->pNext == NULL) /* No following nodes. */ {
      pEntryAt->pNext = pNewEntry;
      pNewEntry->pNext = NULL;
    } else {
      pNewEntry->pNext = pEntryAt->pNext;
      pEntryAt->pNext = pNewEntry;
    }
    pNewEntry->pPrev = pEntryAt;
    pNewEntry->Type = Mode;
    return TRUE;
  }
  
  void FreeMem (void *pPtr)
  {
    if (pPtr != NULL) { free (pPtr); }
  }
  
  void FreeAllMem (void)
  {
    struct ENTRY *pEntry;
    struct ENTRY *pNextEntry;
    pEntry = m_pEntry;
    while (1) {
      if (pEntry == NULL) { break; }
      pNextEntry = pEntry->pNext;
      FreeMem (pEntry->pText); /* Frees the pointer if not NULL */
      FreeMem (pEntry);
      pEntry = pNextEntry;
    }
    m_pEntry = NULL;
    m_pCurEntry = NULL;
  }
  
  bool FindKey (CCHR *pSection, CCHR *pKey, EFIND *pList)
  {
    char Search [130];
    char Found [130];
    char Text [255];
    char *pText;
    struct ENTRY *pEntry;
    pList->pSec = NULL;
    pList->pKey = NULL;
    pEntry = FindSection (pSection);
    if (pEntry == NULL) { return FALSE; }
    pList->pSec = pEntry;
    pList->KeyText[0] = 0;
    pList->ValText[0] = 0;
    pList->Comment[0] = 0;
    pEntry = pEntry->pNext;
    if (pEntry == NULL) { return FALSE; }
    sprintf (Search, "%s",pKey);
    strupr (Search);
    while (pEntry != NULL) {
      if ((pEntry->Type == tpSECTION) || /* Stop after next section or EOF */
        (pEntry->Type == tpNULL )) {
        return FALSE;
      }
      if (pEntry->Type == tpKEYVALUE) {
        strcpy (Text, pEntry->pText);
        pText = strchr (Text, ';');
        if (pText != NULL) {
          strcpy (pList->Comment, pText);
          *pText = 0;
        }
        pText = strchr (Text, '=');
        if (pText != NULL) {
          *pText = 0;
          strcpy (pList->KeyText, Text);
          strcpy (Found, Text);
          *pText = '=';
          strupr (Found);
          /* printf ("%s,%s\n", Search, Found); */
          if (strcmp (Found,Search) == 0) {
            strcpy (pList->ValText, pText+1);
            pList->pKey = pEntry;
            return TRUE;
          }
        }
      }
      pEntry = pEntry->pNext;
    }
    return NULL;
  }

  bool AddSectionAndKey (CCHR *pSection, CCHR *pKey, CCHR *pValue)
  {
    char Text [255];
    sprintf (Text, "[%s]", pSection);
    if (AddItem (tpSECTION, Text) == FALSE) { return FALSE; }
    sprintf (Text, "%s=%s", pKey, pValue);
    return AddItem (tpKEYVALUE, Text)? 1 : 0;
  }
  
  struct ENTRY *MakeNewEntry (void)
  {
    struct ENTRY *pEntry;
    pEntry = (struct ENTRY *)malloc (sizeof (ENTRY));
    if (pEntry == NULL) {
      FreeAllMem ();
      return NULL;
    }
    if (m_pEntry == NULL) {
      m_pEntry = pEntry;
    }
    pEntry->Type = tpNULL;
    pEntry->pPrev = m_pCurEntry;
    pEntry->pNext = NULL;
    pEntry->pText = NULL;
    if (m_pCurEntry != NULL) {
      m_pCurEntry->pNext = pEntry;
    }
    return pEntry;
  }
  
  struct ENTRY *FindSection (CCHR *pSection)
  {
    char Sec [130];
    char iSec [130];
    struct ENTRY *pEntry;
    sprintf (Sec, "[%s]", pSection);
    strupr (Sec);
    pEntry = m_pEntry; /* Get a pointer to the first Entry */
    while (pEntry != NULL) {
      if (pEntry->Type == tpSECTION) {
        strcpy (iSec, pEntry->pText);
        strupr (iSec);
        if (strcmp (Sec, iSec) == 0) {
          return pEntry;
        }
      }
      pEntry = pEntry->pNext;
    }
    return NULL;
  }

  #ifdef DONT_HAVE_STRUPR
  /* DONT_HAVE_STRUPR is set when INI_REMOVE_CR is defined */
  void strupr( char *str )
  {
    // We dont check the ptr because the original also dont do it.
    while (*str != 0) {
      if ( islower( *str ) ) {
        *str = toupper( *str );
      }
      str++;
    }
  }
  #endif
  
private:
};

#endif

#ifdef INIFILE_TEST_THIS_FILE
int main (void)
{
  CIniFile iFile;
  printf ("Hello World\n");
  iFile.OpenIniFile ("Test.Ini");
  iFile.WriteString ("Test", "Name", "Value");
  iFile.WriteString ("Test", "Name", "OverWrittenValue");
  iFile.WriteString ("Test", "Port", "COM1");
  iFile.WriteString ("Test", "User", "James Brown jr.");
  iFile.WriteString ("Configuration", "eDriver", "MBM2.VXD");
  iFile.WriteString ("Configuration", "Wrap", "LPT.VXD");
  iFile.WriteInt ("IO-Port", "Com", 2);
  iFile.WriteBool ("IO-Port", "IsValid", 0);
  iFile.WriteDouble ("TheMoney", "TheMoney", 67892.00241);
  iFile.WriteInt ("Test" , "ToDelete", 1234);
  iFile.WriteIniFile ("Test.Ini");
  printf ("Key ToDelete created. Check ini file. Any key to continue");
  while (!kbhit());
  iFile.OpenIniFile ("Test.Ini");
  iFile.DeleteKey ("Test"   , "ToDelete");
  iFile.WriteIniFile ("Test.Ini");
  printf ("[Test] Name = %s\n", iFile.ReadString ("Test", "Name", "NotFound"));
  printf ("[Test] Port = %s\n", iFile.ReadString ("Test", "Port", "NotFound"));
  printf ("[Test] User = %s\n", iFile.ReadString ("Test", "User", "NotFound"));
  printf ("[Configuration] eDriver = %s\n", iFile.ReadString ("Configuration", "eDriver", "NotFound"));
  printf ("[Configuration] Wrap = %s\n", iFile.ReadString ("Configuration", "Wrap", "NotFound"));
  printf ("[IO-Port] Com = %d\n", iFile.ReadInt ("IO-Port", "Com", 0));
  printf ("[IO-Port] IsValid = %d\n", iFile.ReadBool ("IO-Port", "IsValid", 0));
  printf ("[TheMoney] TheMoney = %1.10lf\n", iFile.ReadDouble ("TheMoney", "TheMoney", 111));
  iFile.CloseIniFile ();
  return 0;
}
#endif

posted @ 2006-05-31 13:20 cyantree 阅读(832) | 评论 (1)编辑 收藏

跨平台的线程代码

改编自fltk,添加了linux平台下的Sleep实现,只支持Windows和Linux,分别用vc和gcc编译,代码如下:

1 //threads.h, LGPL
2
3 #ifndef Threads_H
4 #define Threads_H
5
6 #ifdef WIN32
7
8 #include < windows.h >
9 #include < process.h >
10
11 typedef unsigned long Fl_Thread;
12
13 static int fl_create_thread(Fl_Thread& t, void *(*f) (void *), void* p)
14 {
15 return t = (Fl_Thread)_beginthread((void( __cdecl * )( void * ))f, 0, p);
16 }
17
18 #else
19
20 // Use POSIX threading...
21 #include < pthread.h >
22 #include < unistd.h >
23
24 typedef pthread_t Fl_Thread;
25
26 static int fl_create_thread(Fl_Thread& t, void *(*f) (void *), void* p)
27 {
28 return pthread_create((pthread_t*)&t, 0, f, p);
29 }
30
31 static void Sleep(unsigned long dwMilliseconds)
32 {
33 usleep(dwMilliseconds * 1000);
34 }
35
36 #endif
37
38 #endif // !Threads_h

使用示例:

1 #include "thread.h"
2 ...
3
4 static Fl_Thread m_thread; // define
5 ...
6
7 // thread create
8 fl_create_thread(m_thread, thread_fun, 0);
9 ...
10
11 static void* thread_fun(void *p)
12 {
13 while (1) {
14 ...
15 }
16
17 return 0;
18 }

posted @ 2006-05-16 16:46 cyantree 阅读(906) | 评论 (1)编辑 收藏

从编译Linux kernel说开来

  编译内核的概念是linux最帅的地方,windows是不会提供操作系统的源代码的,而linux则不然,操作系统的源代码全部都是公开的。 对于发行版而言,比如rh9,debian,suse等,都提供编译好的kernel供用户使用,但是这样的kernel未必是用户需要的,这时就可以自 己对 kernel的源代码进行编译,然后替换掉原来的kernel。

  其实编译kernel并不恐怖,只是一件很简单的事情,就几条 指令而已。/usr/src/linnux是kernel源代码常用的放置目录,进入此目录,执行make menuconfig,会出来一个图形界面,里面是kernel的各种配置和选项,把自己喜欢的驱动和配置选上,不需要的去除即可。然后make dep,这是检查新的配置是否正确和做一些初始化工作。然后就是make bzImage,这是真正开始编译kernel,bzImage是编译后的kernel文件名,可以为任意名字,只是一般会以bzImage、 zImage或vmlinuz之类为名。如果编译成功会生成一个bzImage文件,这就是kernel了。系统启动最主要的就是调用这个文件,里面包括 了文件系统、硬件驱动、网络基础、内存管理、进程管理等,在/boot下面有一个bzImage或者vmlinuz之类的文件就是原有的kernel,只 要把编译后的kernel文件(bzImage)替换掉就换成新的kernel了,或者用新名字复制到/boot下,再将grub或lilo的配置文件修 改一下,下次启动系统的时候即可使用新的kernel。下面是make modules,这是编译驱动,或者说叫模块,在linux下驱动可以和kernel编译在一起,也可以编译成单独的模块文件,如果是第一种, kernel启动的时候就会自动尝试载入驱动,第二种则是需要的时候手工加载驱动,也可以手动卸载。这是windows没有的功能,比如显卡的驱动在 windows下不重新启动系统是无法更新的,但在linux下却可以。比如对显卡做升级动作的时候其实就是在编译module,并卸载旧驱动,加载新驱 动,整个步骤无需重启系统。最后一步,就是make modules_install,这个步骤是在/lib/modules下面生成一个按照kernel版本号为格式的目录,再将编译好的modules复 制进去。kernel启动的时候会通过这样的路径搜索modules文件,类似windows的system32目录。

  写了这么多, 很多没玩过linux的人看了会很迷糊,但了解一些相关认识还是很有趣的,因为这才是linux好玩的地方,一个随便捏来捏去的操作系统,可以满足某些人 士的变态心理-_-# windows可以运行的硬件平台很有限,因为不公开,而且尺寸也很大,linux却因为可以自己编译,所以支持几乎所有的硬件平 台,x86,cs51,avr,pda,手机,alpha,大型机等等。同时尺寸还很小,一般编译一个可以运行到shell的系统,只要4、5M的尺寸就 可以搞定。加一个x-window也就30M左右,这在windows是不可想象的。这都是kernel公开,可以手动编译的好处和乐趣....
 

2006-05-10 20:55 (根据聊天纪录修改)

posted @ 2006-05-10 21:10 cyantree 阅读(965) | 评论 (1)编辑 收藏

FLTK简介

  FLTK,如同其名字所表达的:The Fast Light Tool Kit,一个轻量级的GUI开发库。但这轻量级并不代表功能的羸弱,相反,FLTK在具有基本的GUI功能之外,还拥有一些特殊的功能,比如跨平台、内置 OpenGL功能、速度更快、尺寸更小、协议宽松等。当然,缺点也是有的,比如对于复杂的界面构件支持不够,资源支持的不足等。但一个工具如果使用的好, 取其长而去其短,自然可以飞花摘叶皆可伤人;P

   我选择FLTK 的过程还是比较曲折的,当初做ARM下的GUI开发,选择的GUI库是MiniGUI,一个国内开发的界面库。当时还支持类unix平台,对 Windows的支持尚在开发中。由于需要寻找一些问题的解答,所以经常在其论坛上搜索,从而知道了还有microwindow、nano、 qtembedded等嵌入型GUI开发库,但当时没有太过注意。后来又开始转向WINCE平台的开发,这一搁就是2年。再后来终于要做跨平台的开发,对 具有跨平台的GUI开发库开始注意起来。

  一开始的选择是wxWidgets,但是研究了一段时间后发现不好解决的问题越来越多,终于 放弃。最头疼的就是C++类的事件传递,wxWidgets内部使用的是一个类似MFC的方法,所有传递事件的类全部要从一个根类继承,这样就导致创建的 类和wxWidgets绑定过甚,复用性大大降低,同时由于wxWidgets的目标不仅仅是GUI,造成其包含功能过多,其内部结构非常复杂,虽然是 OpenSource,但要若要修改其代码还是很困难的。综上所述,wxWidgets并不符合我的要求,从而被排除在外。

  之后研究的QT,老牌的跨平台GUI开发库,工具很多,开发也很人性化,qtdesign很像Delphi的界面开发方式,代码带有强烈的linux风格,但是看看附带的库文件又实在让人有些泄气,尺寸大,发布麻烦。所以在试用了一段时间后还是放弃了。

   在此期间,其实也看到过一些对FLTK的介绍,但大多数对其评价不高,也就没有注意。直到有一次偶然心血来潮,上http: //www.fltk.org看了一下,发现FLTK 似乎正对我的胃口,这才开始对其进行了深入的研究。经过一段时间的实际开发,个人觉得,对于跨平台和代码简洁而言,FLTK是再适合不过了。

   FLTK的底层只提供一套完整的画点、画线功能,另外附带了字体的显示功能,但FLTK对字体的支持还很粗糙,尤其对于非英文字符集而言,后面我会详细 说明。在基本的点、线功能基础上,FLTK完全自己实现了一套界面,比如Button、Label、Edit、Tab等,全部都是由基本的点线画出。看到 这,可能你会觉得这实在是属于自己造轮子,吃力不讨好。诚然,如果你只针对一种平台开发,这样的做法不能带来多少好处,还造成学习时间的拉长。但若要针对 多个平台开发,这样做的好处就很明显了。首先是移植容易,只要针对目标平台实现基本的点线功能就可以实现代码的移植,这可能是所有跨平台GUI库中最方便 最直接的方案,目前FTLK支持MacOS、Windows、Linux(x-window)等平台,针对WinCE(主要是unicode的问题)和 plam 的开发正在进行中。其次是保持了界面的一致性,虽然QT、GTK等开发库也具有这种功能,但是他们都需要一套基本库的支持,无法做到系统尺寸的优化,而对 于FLTK而言,这却恰恰是他的优点和长项。最后是代码层次清楚、结构简单,由于大部分的工作就是基于底层的点线功能进行自绘,所有很多代码都是简洁明 了,很少费话。

  底层之上是一套以Fl_开头的类,代表了各种GUI构件,比如Fl_Window、Fl_Button、 Fl_Input等,使用起来很是容易。同时由于上面所说的,所有的界面构件都是画出来的,因此在熟悉了这种方式后,生成自己的构件也是很容易的,反正是 画界面嘛,既然别人能做到,你也能做到,实在不行可以查阅源代码进行学习。这些界面类的共同特点是轻量型、都拥有一个draw(),只要在draw()里 实现自己的绘画动作即可。

  说到界面就不能不说其事件实现方式,对于FLTK而言,使用的是最直接的方法:while(1){}。这也 是很多人批评FLTK原始的一个原因。但仔细想想,其实这是最直接的办法,不管是哪种平台,最终的事件方案不外乎是死循环和中断,中断的确具有很多好处, 但只要while(1)能完成这部分的功能,那又有什么关系呢。每个界面类都有一个handle(int event),只要继承这个成员函数,就可以在其中处理自己的事务。是不是很简单?同时由于这样的事件方式,造成FLTK的刷新速度很快,事件反应迅速, 也算是个附带优点了。现在大多数的开发库都是采用OO方式的事件处理方式,但FLTK却采用了最原始的函数指针方式,也算是一个异类,这可能和FLTK的 unix背景有关,无论如何,这种方式还是需要一定的适应时间的,而且这种方式的优缺点也是属于各花入各眼了,不过我本人还是很喜欢这种方式的,谁叫我比 较原始呢#-_-

  FLTK产生于NeXT环境,发展于X-window环境,所以对图形加速的支持必然是选择OpenGL。FLTK 使用Fl_Gl_Window这个类将OpenGL的基本功能囊括其中,只要在Fl_Gl_Window的draw()里glbegin/glend即 可,基本的设置工作FLTK全都做好了,对于我现在的系统要求简直是最适合不过了。

  FLTK基于LGPL,对使用者的要求非常宽松: Contrary to popular belief, it can be used in commercial software - even Bill Gates could use it! 所以开发者不需要担心其项目的隐形问题。但是如果对FLTK进行了卓有成效的修正最好还是能回馈给开发组,所谓我为人人,人人为我嘛。

   说了一堆的好话,现在开始谈谈FLTK的缺点。首先一条就是对非英文字体特别是中文的支持比较差,甚至是非常差。在Windows平台下还好一点,因为 在 Windows平台下使用的是TextOut函数输出字符串,但是在X-Window环境下就完蛋了,无法正确显示中文,也不能调用输入法进行输入。从这 点来说,FLTK还是只适合一些封闭软件的开发,对于通用软件而言FLTK并非是一个好的选择。但只能在Windows平台下开发中文软件也不是个办法, 要解决中文的显示问题也不难,目前FTLK的稳定版是1.1.7,开发版是2.0,有人针对1.xx版本修改了一个unicode版本,可以很平滑的支持 汉字的显示,但很遗憾,我没有编译成功过,如果谁编译成功了可以给我来个信。2.0已经对非英文的支持进行了专门的开发,但目前还没有release,在 不久的将来应该可以完整的解决这个问题。目前,要么等待,要么就像我这样,用点阵字库瞒天过海。具体方法因为还不够成熟,就不公布了。

  无论如何,FLTK的目标还是针对嵌入式和封闭软件的开发,所以复杂的界面并非其长项,如果想做出花哨复杂的界面,还是用其他GUI库比较好,FLTK并不适合。

  综上所述,FLTK的优点和缺点都是非常的突出,如何取舍还是自己决定吧。



2006-04-16 22:02

posted @ 2006-04-16 22:11 cyantree 阅读(20347) | 评论 (20)编辑 收藏

多进程的构架方法

  做了4、5年的开发,大大小小的项目也做了不少,但以前都有一个致命的问题,不知不觉就会写出一个巨大的主程序出来,层次复杂,编码痛苦,调试困难。但似乎大家都认同这样的开发方式,虽然都知道界面和功能分离是好事情,但就是做不到。我自己也曾痛苦的思考过,但没有什么收效,似乎在Windows下的开发只能是这么痛苦。

  一星期前买了<<unix编程艺术>>,这一周可谓改天换地,每天都在阅读和思考中度过,想必武侠小说中的武功大进也就是这个意思了。虽然书还没看完,但是有些话实在是不吐不快。

   什么是界面?界面就是功能的子集。没有哪个界面能反映所有的功能,但是若没有界面,对于最终用户来说又是不可忍受的,无论如何都不能指望让一个门卫学会输入复杂的命令来完成工作,虽然最终用户也包括专业人士,但这世界上终究普通人更多。在这样的前提下,可以认为功能永远比界面更宽泛,更有适应性,而GUI更狭窄,更具有特殊性,所以将界面和功能进行分拆也就成为一种必然趋势。

  但是如何分拆?在Windows的世界里,一个普遍观点就是DLL。DLL很好,但是还不够好,因为无法直接使用、调试以及升级,带来的问题远比好处多。另一种方法就是在代码级进行分层,比如GUI一层,功能一层,再用胶合层将二者整合。且不论胶合层的不可复用和调试困难,就一条,如何能做到GUI崩溃的时候却不影响功能的实现?以前我做过的项目都是这样处理的,直接的后果就是项目越到后期问题越多,代码越不接受变化。调试花费了大量的人力物力,收效却未必好,功能的一点点小修改就会造成代码里出现意大利面条。你可以说只要前期的小心规划和仔细架构就能避免这些问题,但是谁能准确预测未来?无论做怎样的努力,你也不能保证现在的功能永远不变,永远不变的恰恰就是变。如果不能保持实现的稳定性和较好的移植性,这样的项目下场一般都不太好。

  说了这么多废话,还是赶紧进入正题。谈谈这一周来的心得体会!

  首先,变化是渐进的,非突变式的。如果能将变化的所在约束在一个比较小的代码范围内,修改就不会成为噩梦。怎么约束?就一个要求:在保证完整性的条件下让每个模块包含的功能尽量单一和足够小。首先是保证完整性,不是代码足够短,包含的实现足够少就是完整,要达到完整,就要让模块的各个部分做到不可分割和无需添加,按照古人的说法,就是增一分则太多,减一分则太少。这个要求虽然看起来很好理解,其实并没有什么标准答案,每个人心里都有自己的回答,正所谓仁者见仁,智者见智。其次是单一化和小型化,一般来说,范围太大的东西会造成人脑覆盖不全,比如一个功能,如果牵扯的部分过多,就会造成从底层到中间层,再到上层,全部都要思考到,估计没有几个人能做到,即使做到了,将来的维护和修改也会变成噩梦。相反,只要功能的涉及面够窄,就很容易进行思考和修改,这个道理应该没有什么问题。

  既然要保证模块单一、小型化和保证完整,也就意味着这个模块可以认为是一个完整而单独的程序,无需外围程序的支持就可以单独运行和测试。从而引出我的最重要的观点:尽量用多进程来分拆程序。在Windows的世界里,多进程似乎是天生被忽略和鄙视的,从unix的观点看,其主要原因是Windows的设计中对进程的快速创建支持不够,造成对多进程的天然排斥和害怕。但是换一个思路看,多进程也许是目前最好的架构方式。底层的功能分拆成各个进程单独运行,通过ipc和上层的GUI进行交互,胶合层薄了,移植性增强了,调试容易了,功能演进也不再成为噩梦。需求永远是渐变的,所以进程的渐变也就成为可控的行为。

   多进程间的传递方式一般有这么几种:共享内存,管道(pipe),信号,消息, socket。其中共享内存适宜于大量数据的即时传递,速度快,容量大。但使用共享内存时需要仔细考虑读写冲突问题,一般的解决办法是用全局锁,但是锁的存在必然会造成效率的下降,所以能不用锁就尽量不要用。pipe的速度和容量都没有共享内存好,但是用来传递命令和返回值还是很适合的。信号和消息的方式一般会和操作系统联系紧密,个人不太喜欢。最后是socket,对于异地交互而言,socket是目前很常用的手段,甚至本地进程间通讯也可以使用。但是由于和网络有关,所以同步性不好保证,需要辩证的使用。

  说了这么多,举个例子说明一下。假设现在要做一套点菜软件供酒店使用,其基本功能包括人员管理,桌台管理,点菜管理,结账以及后台管理五个功能模块。按照单进程的方式就是将所有功能整合在一起,系统启动时加载所有的功能,一旦某个模块出现问题,则必须重新启动程序,而且各个模块之间很容易发生资源冲突和请求冲突。如果换成多进程方式,让我们看看有什么不同。首先是所有的功能最终目的地都是数据库,那么可以开发一个后台进程专门所有负责针对数据库的请求,通过pipe或者共享内存来接收命令和返回结果,那么程序或者说具体代码块之间的接口就是单一的pipe或共享内存了。同时,即使某一个程序运行错误也不会造成整体失败,只需要重起失败的部分即可。当然了,这种方式下存在一个问题,就是效率的降低,但是对于大多数的应用来说,稳定性的提高远比效率的降低要重要,而且随着硬件水平的不断提高,效率总是可以达标的。

  数据库处理分拆出去后,剩下的就很好处理了,人员、点菜、桌台等管理模块都作为单独的后台程序出现,最后GUI部分只需要和各个共享内存和pipe打交道即可,无需只要具体的逻辑处理和功能实现,而且各个后台程序还可以复用,比如人员管理可以挪到客房服务系统中,甚至是其他系统。GUI随时可以替换,实现了功能和界面的分离,同时系统崩溃的几率大大降低,升级和售后也方便很多,永远不要把最终用户想的太愚蠢,很多时候人们还是蛮有求知欲的。

  更多的细节需要自己整理,这里只是给出了一个框架,起码我现在的项目已经开始这样做,效果嘛,半年后就知道了。 

2006-04-05 21:38 

修改于2006-04-07 18:56

再次修改于2006-04-13 21:55

posted @ 2006-04-14 01:10 cyantree 阅读(3133) | 评论 (12)编辑 收藏

仅列出标题
共2页: 1 2