Allegro学习笔记十七

http://wiki.allegro.cc/AllegroExamples 以上是英文例子站点。

by Shawn Hargreaves,allegro的作者

目录: 1 Allegro 例子

1.1 exhello
1.2 exmem
1.3 expat
1.4 expal
1.5 exflame
1.6 exbuf
1.7 exflip
1.8 exfixed
1.9 exfont
1.10exmouse
1.11extimer
1.12exkeys
1.16exgui
1.17excustoms

/*
* Example program for the Allegro library, by Shawn Hargreaves.
*
* 在上一个例子exgui.c演示了如何将默认的Allegro框架个性化
* 现在将演示一个自定义的会话过程,它显示了一个生动的时钟
* 却没有破坏其他的GUI,另外一个简单的例子演示了如何改变
* 所有GUI元素的字体。
*/


#include <time.h>

#include <allegro.h>
#include "example.h"

/* we need to load example.dat to access the big font */
DATAFILE *datafile;


/* for the d_edit_proc() object */
#define LEN 32
char the_string[(LEN + 1) * 6] = "Change Me!";


/* since we change the font, we need to store a copy of the original one */
FONT *original_font;


/* the current time, for the clock object */
struct tm the_time;

/* 为"change font"按钮自定义的会话处理过程. 这里使用了简单的
* 窗口继承: 他调用d_button_proc()来处理绝大多数事情,因此它
* 看起来就象个按钮,但是当按钮被按下时,d_button_proc()将返回
* D_CLOSE,它(本处理过程)捕获这个消息(D_CLOSE)并改变了字体
*/
int change_font_proc(int msg, DIALOG *d, int c)
{
int ret;

/* 调用父对象 */
ret = d_button_proc(msg, d, c);

/* 替换掉返回值并改变字体 */
if (ret == D_CLOSE) {
if (font == original_font)
font = (FONT *)datafile[BIG_FONT].dat;
else
font = original_font;

return D_REDRAW;
}

/* 否则就直接返回好了 */
return ret;
}

/* 画时钟指针(not pointer:)的帮助器函数 */
void draw_hand(BITMAP *bmp, int value, int range, int v2, int range2, fixed length, int color)
{
fixed angle;
fixed x, y;
int w, h;

angle = ((itofix(value) * 256) / range) +
((itofix(v2) * 256) / (range * range2)) - itofix(64);

x = fixmul(fixcos(angle), length);
y = fixmul(fixsin(angle), length);
w = bmp->w / 2;
h = bmp->h / 2;

line(bmp, w, h, w + fixtoi(x*w), h + fixtoi(y*h), color);
}

/* 时钟对象的自定义会话处理过程 */
int clock_proc(int msg, DIALOG *d, int c)
{
time_t current_time;
struct tm *t;
BITMAP *temp;
fixed angle, x, y;

/* 处理消息 */
switch (msg) {

/* 当我们捕获MSG_START时初始化 */
case MSG_START:
/* 储存当前时间 */
current_time = time(NULL);
t = localtime(&current_time);
the_time = *t;

/* 将时钟背景绘制到一张内存位图上 */
temp = create_bitmap(d->w, d->h);
clear_to_color(temp, d->bg);

/* 绘制表盘和中心 */
circle(temp, temp->w/2, temp->h/2, temp->w/2-1, d->fg);
circlefill(temp, temp->w/2, temp->h/2, 2, d->fg);

/* 在边缘绘制刻度 */
for (angle=0; angle<itofix(256); angle+=itofix(256)/12) {
x = fixcos(angle);
y = fixsin(angle);
line(temp, temp->w/2+fixtoi(x*temp->w*15/32),
temp->h/2+fixtoi(y*temp->w*15/32),
temp->w/2+fixtoi(x*temp->w/2),
temp->h/2+fixtoi(y*temp->w/2), d->fg);
}

/* 将时钟背景位图储存在d->dp */
d->dp = temp;
break;

/* 当我们捕获MSG_END消息时则关闭 */
case MSG_END:
/* 释放背景位图 */
destroy_bitmap((BITMAP *)d->dp);
break;

/* 当捕获空消息时则更新时钟 */
case MSG_IDLE:
/* 读取当前时间 */
current_time = time(NULL);
t = localtime(&current_time);

/* 看看它是不是变了 */
if ((the_time.tm_sec != t->tm_sec) ||
(the_time.tm_min != t->tm_min) ||
(the_time.tm_hour != t->tm_hour)) {
the_time = *t;

/* 如果时间变了就重绘我们自己.注意:当MSG_DRAW发送到某个或全部的对象,
* 会话管理程序将自动隐藏鼠标,因此,我们不必自己去做这个事.同样需要
* 注意的是:使用object_message向clock_proc()发送消息要比一个简单的
* 回调要好的多. 这个通过(会话)对象调用的函数指针,允许其他对象处理
* 过程钩住它(hook),举个例子,一个完全不同的时钟可以自己处理MSG_DRAW
* 消息,但是把MSG_IDLE消息传递给这个处理过程(clock_proc)
*/
object_message(d, MSG_DRAW, 0);
}
break;

/* 响应MSG_DRAW,绘制时钟 */
case MSG_DRAW:
/* 在一个临时的内存位图上绘制以消除闪烁 */
temp = create_bitmap(d->w, d->h);

/* copy the clock background onto the temporary bitmap */
blit((BITMAP*)d->dp, temp, 0, 0, 0, 0, d->w, d->h);

/* draw the hands */
draw_hand(temp, the_time.tm_sec, 60, 0, 1, itofix(9)/10, d->fg);
draw_hand(temp, the_time.tm_min, 60, the_time.tm_sec, 60, itofix(5)/6, d->fg);
draw_hand(temp, the_time.tm_hour, 12, the_time.tm_min, 60, itofix(1)/2, d->fg);

/* copy the temporary bitmap onto the screen */
blit(temp, screen, 0, 0, d->x, d->y, d->w, d->h);
destroy_bitmap(temp);
break;
}

/* 只要时钟会话没要求关闭,或者没有获得输入焦点就总是返回OK状态 */
return D_O_K;
}

DIALOG the_dialog[] =
{
/* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */
{ d_clear_proc, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, NULL, NULL, NULL },
{ d_edit_proc, 12, 82, 256, 48, 255, 0, 0, 0, LEN, 0, the_string, NULL, NULL },
{ d_check_proc, 12, 12, 161, 49, 255, 0, 't', 0, 0, 0, "&Toggle Me", NULL, NULL },
{ clock_proc, 242, 12, 64, 64, 255, 0, 0, 0, 0, 0, NULL, NULL, NULL },
{ change_font_proc, 12, 142, 141, 49, 255, 0, 'f', D_EXIT, 0, 0, "Change &Font", NULL, NULL },
{ d_button_proc, 162, 142, 141, 49, 255, 0, 0, D_EXIT, 0, 0, "Exit", NULL, NULL },
{ NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }
};

int main(int argc, char *argv[])
{
int item;
char buf[256];

if (allegro_init() != 0)
return 1;
install_keyboard();
install_mouse();
install_timer();

if (set_gfx_mode(GFX_AUTODETECT, 320, 200, 0, 0) != 0) {
if (set_gfx_mode(GFX_SAFE, 320, 200, 0, 0) != 0) {
set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
allegro_message("Unable to set any graphic mode\n%s\n", allegro_error);
return 1;
}
}

set_palette(desktop_palette);

/* We set up colors to match screen color depth (in case it changed) */
for (item = 0; the_dialog[item].proc; item++) {
the_dialog[item].fg = makecol(0, 0, 0);
the_dialog[item].bg = makecol(255, 255, 255);
}

/* load the datafile */
replace_filename(buf, argv[0], "example.dat", sizeof(buf));
datafile = load_datafile(buf);
if (!datafile) {
set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
allegro_message("Error loading %s!\n", buf);
return 1;
}

/* store a copy of the default font */
original_font = font;

do_dialog(the_dialog, -1);

unload_datafile(datafile);

return 0;
}

END_OF_MAIN()
---------------------------------------------------------------------------
小结17:
PS:经过上个例子的深入研究,很多未果的问题在这个例子里逐渐清晰。

1、int change_font_proc(int msg, DIALOG *d, int c)
{
int ret;
/* 调用父对象 */
ret = d_button_proc(msg, d, c);
/* 替换掉返回值并改变字体 */
if (ret == D_CLOSE) {
if (font == original_font)
font = (FONT *)datafile[BIG_FONT].dat;
else
font = original_font;
return D_REDRAW;
}
/* 否则就直接返回好了 */
return ret;
}
ret = d_button_proc(msg, d, c); 这个技巧被明确的解释为"调用父对象"。也就是说使用d_button_proc来处理绝大多数消息,这个技巧类似在windows编程中,将Winporc的参数传递给我们自己的消息响应函数。因为大量的时候是MSG_IDLE,而此时btnChageFont没什么"特别的"事情可做--并不是说它完全不做事情。它唯一需要做"特别"事情的时候是当有MSG_CLICK被发送到消息队列,而我们可以根据d_button_proc的返回值来得到:因为最开始设置了标志量D_EXIT,所以返回值为D_COLSE,也可以更换标志量,相应的则是需要更换处理的返回值。

2、time_t current_time;
struct tm *t;
这是两个跟时间相关的变量,来自Microsoft。

3、object_message(d, MSG_DRAW, 0);
这句包含着理解Allegro消息机制的核心。他是宏SEND_MESSAGE()的原形,作用是对Dialog对象发送消息。
现在再来回头想想上个例子里的几个名词:标志量、消息、返回值。他们之间的关系:

标志量将影响返回值:在上个例子就理解了。

返回值将影响do_dialog();
消息将影响*_*_porc();
第2点和第3点密不可分:根据返回值将产生一个消息,这个消息由do_dialog发送到DIALOG[]的全部对象(在哪里产生的还不确定,很有可能是do_dialog()产生的)。在返回值为D_O_K的情况下,do_dialog会发送默认消息,这个消息是用户输入产生的,如果没有输入,就是MSG_IDLE消息。这里便有个消息优先级的概念:
会话处理过程产生的非MSG_IDLE消息将立刻发送
用户输入产生的消息优先级其次
最后为MSG_IDLE消息
因此消息处理流程为:
while(true)
{
if(!用户输入产生消息)
{
do_dialog()转发,并接收返回值(one by one模式,非broadcast模式)
{
while(返回值!=D_O_K)
{
处理返回值并生成消息;
do_dialog()转发,并接收返回值(one by one模式,非broadcast模式);
}
}
}
}
Allegro消息轮询的实质可能就是这个样子。但是有个弊端:如果2个对象在设置上是同一时刻响应同一个用户输入或消息,比如2个按钮具有相同的热键,那么最先被询问的对象将响应此输入(或消息),在它后面的对象就得不到这个消息了。解决的方法是在最先响应的对象里向其他需要同一时间响应的对象发送消息。

PS:作者就是作者呀,例子比前一个要深入不止一点~

你可能感兴趣的:(编程,C++,c,Microsoft,C#)