1 /*
2 * XPilotNG/SDL, an SDL/OpenGL XPilot client.
3 *
4 * Copyright (C) 2003-2004 by
5 *
6 * Juha Lindstr�m <juhal@users.sourceforge.net>
7 * Darel Cullen <darelcullen@users.sourceforge.net>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include "xpclient_sdl.h"
25
26 #include "sdlmeta.h"
27 #include "sdlwindow.h"
28 #include "text.h"
29 #include "glwidgets.h"
30
31 #define EVENT_JOIN 0
32 #define EVENT_REFRESH 1
33
34 #define SELECTED_BG 0x009000ff
35 #define ROW_FG 0xffff00ff
36 #define ROW_BG1 0x0000a0ff
37 #define ROW_BG2 0x000070ff
38 #define HEADER_FG 0xffff00ff
39 #define HEADER_BG 0xff0000ff
40 #define STATUS_ROWS 7
41 #define STATUS_COLS 4
42 #define STATUS_FIELD_FG 0xffff00ff
43 #define STATUS_FIELD_BG 0xff0000ff
44 #define STATUS_VALUE_FG 0xffff00ff
45 #define STATUS_VALUE_BG 0x000070ff
46 #define PLIST_HEADER_FG 0xffff00ff
47 #define PLIST_HEADER_BG 0xff0000ff
48 #define PLIST_ITEM_FG 0xffff00ff
49 #define PLIST_ITEM_BG 0x000070ff
50 #define ROW_HEIGHT 19
51 #define VERSION_WIDTH 100
52 #define COUNT_WIDTH 20
53 #define META_WIDTH 837
54 #define META_HEIGHT 768
55 #define BUTTON_BG 0xff0000ff
56 #define BUTTON_FG 0xffff00ff
57
58 #define METAWIDGET 100
59 #define METATABLEWIDGET 101
60 #define METAROWWIDGET 102
61 #define METAHEADERWIDGET 103
62 #define STATUSWIDGET 104
63 #define PLAYERLISTWIDGET 105
64
65 static int status_column_widths[] = { 100, 0, 100, 70 };
66
67 typedef struct {
68 GLWidget *table;
69 GLWidget *status;
70 GLWidget *players;
71 GLuint texture;
72 texcoord_t txc;
73 } MetaWidget;
74
75 typedef struct {
76 list_t server_list;
77 GLWidget *meta;
78 GLWidget *scrollbar;
79 GLWidget *header;
80 struct _MetaRowWidget *selected;
81 struct _MetaRowWidget *first_row;
82 } MetaTableWidget;
83
84 typedef struct _MetaRowWidget {
85 Uint32 fg;
86 Uint32 bg;
87 MetaTableWidget *table;
88 server_info_t *sip;
89 bool is_selected;
90 } MetaRowWidget;
91
92 typedef struct {
93 Uint32 fg;
94 Uint32 bg;
95 } MetaHeaderWidget;
96
97 typedef struct {
98 Uint32 name_fg, name_bg, value_fg, value_bg;
99 char address_str[22];
100 char fps_str[4];
101 server_info_t *sip;
102 } StatusWidget;
103
104 typedef struct {
105 Uint32 header_fg, header_bg, item_fg, item_bg;
106 GLWidget *header;
107 GLWidget *scrollbar;
108 list_t players;
109 char *players_str;
110 } PlayerListWidget;
111
112
Scroll_PlayerListWidget(GLfloat pos,void * data)113 static void Scroll_PlayerListWidget(GLfloat pos, void *data)
114 {
115 PlayerListWidget *info;
116 GLWidget *widget, *row;
117 SDL_Rect b;
118 int y;
119
120 widget = (GLWidget*)data;
121 if (widget->WIDGET != PLAYERLISTWIDGET) {
122 error("expected PLAYERLISTWIDGET got [%d]", widget->WIDGET);
123 return;
124 }
125
126 info = (PlayerListWidget*)widget->wid_info;
127 y = widget->bounds.y + ROW_HEIGHT
128 - (int)(List_size(info->players) * ROW_HEIGHT * pos);
129
130 for (row = widget->children; row; row = row->next) {
131 if (row->WIDGET == LABELWIDGET && row != info->header) {
132 b = row->bounds;
133 b.y = y;
134 SetBounds_GLWidget(row, &b);
135 y += ROW_HEIGHT;
136 }
137 }
138 }
139
SetBounds_PlayerListWidget(GLWidget * widget,SDL_Rect * b)140 static void SetBounds_PlayerListWidget(GLWidget *widget, SDL_Rect *b)
141 {
142 int y;
143 GLWidget *row;
144 PlayerListWidget *info;
145 GLfloat list_height;
146 SDL_Rect *wb, sb, rb, hb;
147
148 if (widget->WIDGET != PLAYERLISTWIDGET) {
149 error("expected PLAYERLISTWIDGET got [%d]", widget->WIDGET);
150 return;
151 }
152
153 widget->bounds = *b;
154 info = (PlayerListWidget*)widget->wid_info;
155 list_height = List_size(info->players) * ROW_HEIGHT + ROW_HEIGHT;
156
157 if (info->scrollbar != NULL) {
158 DelGLWidgetListItem(&(widget->children), info->scrollbar);
159 Close_Widget(&(info->scrollbar));
160 info->scrollbar = NULL;
161 }
162
163 y = b->y + ROW_HEIGHT;
164 for (row = widget->children; row; row = row->next) {
165 if (row->WIDGET == LABELWIDGET && row != info->header) {
166 rb.x = b->x + 1;
167 rb.y = y;
168 rb.w = b->w - 11;
169 rb.h = ROW_HEIGHT;
170 SetBounds_GLWidget(row, &rb);
171 y += ROW_HEIGHT;
172 }
173 }
174
175 if (list_height > b->h) {
176 info->scrollbar =
177 Init_ScrollbarWidget(false, 0.0f, ((GLfloat)b->h) / list_height,
178 SB_VERTICAL, Scroll_PlayerListWidget, widget);
179 if (info->scrollbar != NULL) {
180 wb = &(widget->bounds);
181 sb.x = wb->x + wb->w - 10;
182 sb.y = wb->y + ROW_HEIGHT;
183 sb.w = 10;
184 sb.h = wb->h - ROW_HEIGHT;
185 SetBounds_GLWidget(info->scrollbar, &sb);
186 AppendGLWidgetList(&(widget->children), info->scrollbar);
187 } else {
188 error("failed to create a scroll bar for player list");
189 return;
190 }
191 }
192 if (info->header != NULL) {
193 hb.x = widget->bounds.x + 1;
194 hb.y = widget->bounds.y - 1;
195 hb.w = widget->bounds.w - 10;
196 hb.h = ROW_HEIGHT;
197 SetBounds_GLWidget(info->header, &hb);
198 }
199 }
200
create_player_list(char * players_str)201 static list_t create_player_list(char *players_str)
202 {
203 list_t players;
204 char *t;
205
206 if (!(players = List_new())) return NULL;
207 for (t = strtok(players_str, ","); t; t = strtok(NULL, ","))
208 if (!(List_push_back(players, t)))
209 break;
210
211 return players;
212 }
213
Close_PlayerListWidget(GLWidget * widget)214 static void Close_PlayerListWidget(GLWidget *widget)
215 {
216 PlayerListWidget *info;
217
218 if (widget->WIDGET != PLAYERLISTWIDGET) {
219 error("expected PLAYERLISTWIDGET got [%d]", widget->WIDGET);
220 return;
221 }
222
223 info = (PlayerListWidget*)widget->wid_info;
224 if (info->players_str) free(info->players_str);
225 if (info->players) List_delete(info->players);
226 info->players_str = NULL;
227 info->players = NULL;
228 }
229
Paint_PlayerListWidget(GLWidget * widget)230 static void Paint_PlayerListWidget(GLWidget *widget)
231 {
232 SDL_Rect *b = &(widget->bounds);
233 set_alphacolor(PLIST_ITEM_BG);
234 glBegin(GL_QUADS);
235 glVertex2i(b->x, b->y);
236 glVertex2i(b->x + b->w - 10, b->y);
237 glVertex2i(b->x + b->w - 10, b->y + b->h);
238 glVertex2i(b->x, b->y + b->h);
239 glEnd();
240 }
241
Init_PlayerListWidget(server_info_t * sip)242 static GLWidget *Init_PlayerListWidget(server_info_t *sip)
243 {
244 char *player, *players_str;
245 list_t players;
246 list_iter_t iter;
247 GLWidget *tmp, *header, *row;
248 PlayerListWidget *info;
249
250 if (!(players_str = strdup(sip->playlist))) {
251 error("out of memory");
252 return NULL;
253 }
254 if (!(players = create_player_list(players_str))) {
255 error("failed to create players list");
256 free(players_str);
257 return NULL;
258 }
259 if (!(tmp = Init_EmptyBaseGLWidget())) {
260 error("Widget init failed");
261 free(players_str);
262 List_delete(players);
263 return NULL;
264 }
265 if (!(info = (PlayerListWidget*)malloc(sizeof(PlayerListWidget)))) {
266 error("out of memory");
267 free(players_str);
268 List_delete(players);
269 free(tmp);
270 return NULL;
271 }
272 if (!(header =
273 Init_LabelWidget("Players",
274 &(info->header_fg),
275 &(info->header_bg),
276 CENTER,CENTER))) {
277 error("failed to create header for player list");
278 free(players_str);
279 List_delete(players);
280 free(tmp);
281 free(info);
282 return NULL;
283 }
284
285 info->players_str = players_str;
286 info->players = players;
287 info->scrollbar = NULL;
288 info->header = header;
289 info->header_fg = PLIST_HEADER_FG;
290 info->header_bg = PLIST_HEADER_BG;
291 info->item_fg = PLIST_ITEM_FG;
292 info->item_bg = PLIST_ITEM_BG;
293
294 tmp->wid_info = info;
295 tmp->WIDGET = PLAYERLISTWIDGET;
296 tmp->SetBounds = SetBounds_PlayerListWidget;
297 tmp->Draw = Paint_PlayerListWidget;
298 tmp->Close = Close_PlayerListWidget;
299
300 for (iter = List_begin(players);
301 iter != List_end(players);
302 LI_FORWARD(iter)) {
303 player = (char*)SI_DATA(iter);
304 row = Init_LabelWidget(player,
305 &(info->item_fg),
306 &(info->item_bg),
307 LEFT,CENTER);
308 if (!row) break;
309 AppendGLWidgetList(&(tmp->children), row);
310 }
311 AppendGLWidgetList(&(tmp->children), header);
312
313 return tmp;
314 }
315
compute_layout(SDL_Rect * wb,int * width,int * xoff)316 static void compute_layout(SDL_Rect *wb, int *width, int *xoff)
317 {
318 int i, x, w, free_space, num_dynamic, w_dynamic;
319
320 num_dynamic = 0;
321 free_space = wb->w;
322 for (i = 0; i < STATUS_COLS; i++) {
323 w = status_column_widths[i];
324 if (w > 5) {
325 free_space -= w;
326 } else {
327 num_dynamic++;
328 }
329 }
330 w_dynamic = free_space / num_dynamic - 5;
331 x = 0;
332 for (i = 0; i < STATUS_COLS; i++) {
333 w = status_column_widths[i];
334 xoff[i] = x;
335 width[i] = w > 5 ? w - 5 : w_dynamic;
336 x += width[i] + 5;
337 }
338 }
339
SetBounds_StatusWidget(GLWidget * widget,SDL_Rect * wb)340 static void SetBounds_StatusWidget(GLWidget *widget, SDL_Rect *wb)
341 {
342 int i, c, rowc, rowi, colc, coli;
343 int col_width[4];
344 int col_xoff[4];
345 SDL_Rect b;
346 GLWidget *w;
347
348 widget->bounds = *wb;
349 compute_layout(wb, col_width, col_xoff);
350
351 for (c = 0, w = widget->children; w; w = w->next) c++;
352
353 rowc = STATUS_ROWS;
354 colc = STATUS_COLS;
355 b.h = ROW_HEIGHT;
356
357 for (i = 0, w = widget->children; w && i < colc * rowc; w = w->next) {
358 coli = (i % 2) + (i / 2 / rowc) * 2;
359 rowi = (i / 2) % rowc;
360 b.w = col_width[coli];
361 b.x = wb->x + col_xoff[coli];
362 b.y = wb->y + rowi * ROW_HEIGHT;
363 SetBounds_GLWidget(w, &b);
364 i++;
365 }
366 }
367
add_status_entry(const char * name,char * value,GLWidget * parent)368 static void add_status_entry(const char *name, char *value, GLWidget *parent)
369 {
370 GLWidget *name_label, *value_label;
371 StatusWidget *info;
372
373 info = (StatusWidget*)parent->wid_info;
374
375 if ((name_label =
376 Init_LabelWidget(name,
377 &(info->name_fg),
378 &(info->name_bg),
379 LEFT,CENTER))) {
380 AppendGLWidgetList(&(parent->children), name_label);
381 }
382 if ((value_label =
383 Init_LabelWidget(value,
384 &(info->value_fg),
385 &(info->value_bg),
386 LEFT,CENTER))) {
387 AppendGLWidgetList(&(parent->children), value_label);
388 }
389 }
390
Init_StatusWidget(server_info_t * sip)391 static GLWidget *Init_StatusWidget(server_info_t *sip)
392 {
393 GLWidget *tmp;
394 StatusWidget *info;
395
396 if (!(tmp = Init_EmptyBaseGLWidget())) {
397 error("Widget init failed");
398 return NULL;
399 }
400 if (!(info = (StatusWidget*)malloc(sizeof(StatusWidget)))) {
401 error("out of memory");
402 free(tmp);
403 return NULL;
404 }
405 sprintf(info->address_str, "%s:%u", sip->ip_str, sip->port);
406 sprintf(info->fps_str, "%u", sip->fps);
407 info->sip = sip;
408 info->name_fg = STATUS_FIELD_FG;
409 info->name_bg = STATUS_FIELD_BG;
410 info->value_fg = STATUS_VALUE_FG;
411 info->value_bg = STATUS_VALUE_BG;
412 tmp->wid_info = info;
413 tmp->WIDGET = STATUSWIDGET;
414 tmp->SetBounds = SetBounds_StatusWidget;
415
416 add_status_entry(" Server", sip->hostname, tmp);
417 add_status_entry(" Address", info->address_str, tmp);
418 add_status_entry(" Version", sip->version, tmp);
419 add_status_entry(" Map name", sip->mapname, tmp);
420 add_status_entry(" Map size", sip->mapsize, tmp);
421 add_status_entry(" Map author", sip->author, tmp);
422 add_status_entry(" Status", sip->status, tmp);
423 add_status_entry(" Bases", sip->bases_str, tmp);
424 add_status_entry(" Teams", sip->teambases_str, tmp);
425 add_status_entry(" Free bases", sip->freebases, tmp);
426 add_status_entry(" Queue", sip->queue_str, tmp);
427 add_status_entry(" FPS", info->fps_str, tmp);
428 add_status_entry(" Sound", sip->sound, tmp);
429 add_status_entry(" Timing", sip->timing, tmp);
430
431 return tmp;
432 }
433
SelectRow_MetaWidget(GLWidget * widget,MetaRowWidget * row)434 static void SelectRow_MetaWidget(GLWidget *widget, MetaRowWidget *row)
435 {
436 MetaWidget *meta;
437 MetaTableWidget *table;
438 SDL_Rect status_bounds, plist_bounds;
439
440 if (widget->WIDGET != METAWIDGET) {
441 error("expected METAWIDGET got [%d]", widget->WIDGET);
442 return;
443 }
444 meta = (MetaWidget*)widget->wid_info;
445 if (meta->status != NULL) {
446 DelGLWidgetListItem(&(widget->children), meta->status);
447 Close_Widget(&(meta->status));
448 meta->status = NULL;
449 }
450 status_bounds.x = widget->bounds.x + 22;
451 status_bounds.y = widget->bounds.y + 598;
452 status_bounds.w = 792 * 3 / 5;
453 status_bounds.h = ROW_HEIGHT * STATUS_ROWS;
454 if ((meta->status = Init_StatusWidget(row->sip))) {
455 SetBounds_GLWidget(meta->status, &status_bounds);
456 AppendGLWidgetList(&(widget->children), meta->status);
457 } else {
458 error("failed to create a status widget");
459 }
460 if (meta->players != NULL) {
461 DelGLWidgetListItem(&(widget->children), meta->players);
462 Close_Widget(&(meta->players));
463 meta->players = NULL;
464 }
465 plist_bounds.x = status_bounds.x + status_bounds.w;
466 plist_bounds.y = status_bounds.y;
467 plist_bounds.h = status_bounds.h;
468 plist_bounds.w = 792 - status_bounds.w;
469 if ((meta->players = Init_PlayerListWidget(row->sip))) {
470 SetBounds_GLWidget(meta->players, &plist_bounds);
471 AppendGLWidgetList(&(widget->children), meta->players);
472 } else {
473 error("failed to create a player list widget");
474 }
475 row->is_selected = true;
476 table = (MetaTableWidget*)meta->table->wid_info;
477 if (table->selected)
478 table->selected->is_selected = false;
479 table->selected = row;
480 }
481
GetSelectedServer_MetaWidget(GLWidget * widget)482 static server_info_t *GetSelectedServer_MetaWidget(GLWidget *widget)
483 {
484 MetaWidget *meta;
485
486 if (widget->WIDGET != METAWIDGET) {
487 error("expected METAWIDGET got [%d]", widget->WIDGET);
488 return NULL;
489 }
490 meta = (MetaWidget*)widget->wid_info;
491 return ((MetaTableWidget*)meta->table->wid_info)->selected->sip;
492 }
493
Paint_MetaRowWidget(GLWidget * widget)494 static void Paint_MetaRowWidget(GLWidget *widget)
495 {
496 SDL_Rect *b;
497 MetaRowWidget *row;
498
499 if (widget->WIDGET != METAROWWIDGET) {
500 error("expected METAROWWIDGET got [%d]", widget->WIDGET);
501 return;
502 }
503
504 b = &(widget->bounds);
505 row = (MetaRowWidget*)widget->wid_info;
506 set_alphacolor(row->is_selected ? SELECTED_BG : row->bg);
507
508 glBegin(GL_QUADS);
509 glVertex2i(b->x, b->y);
510 glVertex2i(b->x + b->w, b->y);
511 glVertex2i(b->x + b->w, b->y + b->h);
512 glVertex2i(b->x, b->y + b->h);
513 glEnd();
514 }
515
SetBounds_MetaRowWidget(GLWidget * row,SDL_Rect * rb)516 static void SetBounds_MetaRowWidget(GLWidget *row, SDL_Rect *rb)
517 {
518 int free_width;
519 SDL_Rect cb;
520 GLWidget *col;
521
522 row->bounds = *rb;
523 free_width = MAX(rb->w - (VERSION_WIDTH + COUNT_WIDTH), 0);
524
525 if (!(col = row->children)) return;
526 cb.x = rb->x;
527 cb.w = free_width / 2;
528 cb.y = rb->y; cb.h = rb->h;
529 SetBounds_GLWidget(col, &cb);
530
531 if (!(col = col->next)) return;
532 cb.x = cb.x + cb.w;
533 cb.y = rb->y; cb.h = rb->h;
534 SetBounds_GLWidget(col, &cb);
535
536 if (!(col = col->next)) return;
537 cb.x = cb.x + cb.w;
538 cb.y = rb->y; cb.h = rb->h;
539 cb.w = VERSION_WIDTH;
540 SetBounds_GLWidget(col, &cb);
541
542 if (!(col = col->next)) return;
543 cb.x = cb.x + cb.w;
544 cb.y = rb->y; cb.h = rb->h;
545 cb.w = COUNT_WIDTH;
546 SetBounds_GLWidget(col, &cb);
547 }
548
Button_MetaRowWidget(Uint8 button,Uint8 state,Uint16 x,Uint16 y,void * data)549 static void Button_MetaRowWidget(Uint8 button, Uint8 state, Uint16 x,
550 Uint16 y, void *data)
551 {
552 GLWidget *widget;
553 MetaRowWidget *row;
554 SDL_Event evt;
555
556 if (state != SDL_PRESSED) return;
557 if (button != 1) return;
558
559 widget = (GLWidget*)data;
560 if (widget->WIDGET != METAROWWIDGET) {
561 error("expected METAROWWIDGET got [%d]", widget->WIDGET);
562 return;
563 }
564
565 row = (MetaRowWidget*)widget->wid_info;
566 if (row->is_selected) {
567 evt.type = SDL_USEREVENT;
568 evt.user.code = EVENT_JOIN;
569 evt.user.data1 = row->sip;
570 SDL_PushEvent(&evt);
571 } else {
572 SelectRow_MetaWidget(row->table->meta, row);
573 #if 0
574 printf("version: %s\n", row->sip->version);
575 printf("hostname: %s\n", row->sip->hostname);
576 printf("users_str: %s\n", row->sip->users_str);
577 printf("mapname: %s\n", row->sip->mapname);
578 printf("mapsize: %s\n", row->sip->mapsize);
579 printf("author: %s\n", row->sip->author);
580 printf("status: %s\n", row->sip->status);
581 printf("bases_str: %s\n", row->sip->bases_str);
582 printf("fps_str: %s\n", row->sip->fps_str);
583 printf("playlist: %s\n", row->sip->playlist);
584 printf("sound: %s\n", row->sip->sound);
585 printf("teambases_str: %s\n", row->sip->teambases_str);
586 printf("timing: %s\n", row->sip->timing);
587 printf("ip_str: %s\n", row->sip->ip_str);
588 printf("freebases: %s\n", row->sip->freebases);
589 printf("queue_str: %s\n", row->sip->queue_str);
590 printf("domain: %s\n", row->sip->domain);
591 printf("pingtime_str: %s\n", row->sip->pingtime_str);
592 printf("port: %u\n", row->sip->port);
593 printf("ip: %u\n", row->sip->ip);
594 printf("users: %u\n", row->sip->users);
595 printf("bases: %u\n", row->sip->bases);
596 printf("fps: %u\n", row->sip->fps);
597 printf("uptime: %u\n", row->sip->uptime);
598 printf("queue: %u\n", row->sip->queue);
599 printf("pingtime: %u\n", row->sip->pingtime);
600 printf("serial: %c\n", row->sip->serial);
601 #endif
602 }
603 }
604
Init_MetaRowWidget(server_info_t * sip,MetaTableWidget * table,bool is_selected,unsigned int bg)605 static GLWidget *Init_MetaRowWidget(server_info_t *sip,
606 MetaTableWidget *table,
607 bool is_selected,
608 unsigned int bg)
609 {
610 GLWidget *tmp, *col;
611 MetaRowWidget *row;
612
613 if (!(tmp = Init_EmptyBaseGLWidget())) {
614 error("Widget init failed");
615 return NULL;
616 }
617 if (!(row = (MetaRowWidget*)malloc(sizeof(MetaRowWidget)))) {
618 error("out of memory");
619 free(tmp);
620 return NULL;
621 }
622 row->fg = ROW_FG;
623 row->bg = bg;
624 row->sip = sip;
625 row->table = table;
626 row->is_selected = is_selected;
627 tmp->wid_info = row;
628 tmp->WIDGET = METAROWWIDGET;
629 tmp->Draw = Paint_MetaRowWidget;
630 tmp->SetBounds = SetBounds_MetaRowWidget;
631 tmp->button = Button_MetaRowWidget;
632 tmp->buttondata = tmp;
633
634 #define COLUMN(TEXT) \
635 if ((col = Init_LabelWidget((TEXT), &(row->fg), NULL, LEFT, CENTER))) { \
636 col->button = Button_MetaRowWidget; \
637 col->buttondata = tmp; \
638 AppendGLWidgetList(&(tmp->children), col); \
639 }
640 COLUMN(sip->hostname);
641 COLUMN(sip->mapname);
642 COLUMN(sip->version);
643 COLUMN(sip->users_str);
644 #undef COLUMN
645
646 return tmp;
647 }
648
Init_MetaHeaderWidget(void)649 static GLWidget *Init_MetaHeaderWidget(void)
650 {
651 GLWidget *tmp, *col;
652 MetaHeaderWidget *info;
653
654 if (!(tmp = Init_EmptyBaseGLWidget())) {
655 error("Widget init failed");
656 return NULL;
657 }
658 if (!(info = (MetaHeaderWidget*)malloc(sizeof(MetaHeaderWidget)))) {
659 error("out of memory");
660 free(tmp);
661 return NULL;
662 }
663 info->fg = HEADER_FG;
664 info->bg = HEADER_BG;
665 tmp->wid_info = info;
666 tmp->WIDGET = METAHEADERWIDGET;
667 tmp->SetBounds = SetBounds_MetaRowWidget;
668
669 #define HEADER(TEXT) \
670 if ((col = Init_LabelWidget((TEXT), &(info->fg), &(info->bg), LEFT, CENTER))) { \
671 AppendGLWidgetList(&(tmp->children), col); \
672 }
673 HEADER("Server");
674 HEADER("Map");
675 HEADER("Version");
676 HEADER("Pl");
677 #undef COLUMN
678
679 return tmp;
680 }
681
Scroll_MetaTableWidget(GLfloat pos,void * data)682 static void Scroll_MetaTableWidget(GLfloat pos, void *data)
683 {
684 MetaTableWidget *info;
685 GLWidget *widget, *row;
686 SDL_Rect b;
687 int y;
688
689 widget = (GLWidget*)data;
690 if (widget->WIDGET != METATABLEWIDGET) {
691 error("expected METATABLEWIDGET got [%d]", widget->WIDGET);
692 return;
693 }
694
695 info = (MetaTableWidget*)widget->wid_info;
696 y = widget->bounds.y + ROW_HEIGHT
697 - (int)(List_size(info->server_list) * ROW_HEIGHT * pos);
698
699 for (row = widget->children; row; row = row->next) {
700 if (row->WIDGET == METAROWWIDGET) {
701 b = row->bounds;
702 b.y = y;
703 SetBounds_GLWidget(row, &b);
704 y += ROW_HEIGHT;
705 }
706 }
707 }
708
SetBounds_MetaTableWidget(GLWidget * widget,SDL_Rect * b)709 static void SetBounds_MetaTableWidget(GLWidget *widget, SDL_Rect *b)
710 {
711 int y;
712 GLWidget *row;
713 MetaTableWidget *info;
714 GLfloat table_height;
715 SDL_Rect *wb, sb, rb, hb;
716
717 if (widget->WIDGET != METATABLEWIDGET) {
718 error("expected METATABLEWIDGET got [%d]", widget->WIDGET);
719 return;
720 }
721
722 widget->bounds = *b;
723 info = (MetaTableWidget*)widget->wid_info;
724 table_height = List_size(info->server_list) * ROW_HEIGHT;
725
726 if (info->scrollbar != NULL) {
727 DelGLWidgetListItem(&(widget->children), info->scrollbar);
728 Close_Widget(&(info->scrollbar));
729 info->scrollbar = NULL;
730 }
731
732 y = b->y + ROW_HEIGHT;
733 for (row = widget->children; row; row = row->next) {
734 if (row->WIDGET == METAROWWIDGET) {
735 rb.x = b->x;
736 rb.y = y;
737 rb.w = b->w - 10;
738 rb.h = ROW_HEIGHT;
739 SetBounds_GLWidget(row, &rb);
740 y += ROW_HEIGHT;
741 }
742 }
743
744 if (table_height > b->h) {
745 info->scrollbar =
746 Init_ScrollbarWidget(false, 0.0f, ((GLfloat)b->h) / table_height,
747 SB_VERTICAL, Scroll_MetaTableWidget, widget);
748 if (info->scrollbar != NULL) {
749 wb = &(widget->bounds);
750 sb.x = wb->x + wb->w - 10;
751 sb.y = wb->y + ROW_HEIGHT;
752 sb.w = 10;
753 sb.h = wb->h - ROW_HEIGHT;
754 SetBounds_GLWidget(info->scrollbar, &sb);
755 AppendGLWidgetList(&(widget->children), info->scrollbar);
756 } else {
757 error("failed to create a scroll bar for meta table");
758 return;
759 }
760 }
761 if (info->header != NULL) {
762 hb.x = widget->bounds.x;
763 hb.y = widget->bounds.y - 1; /* don't ask why */
764 hb.w = widget->bounds.w - 10;
765 hb.h = ROW_HEIGHT;
766 SetBounds_GLWidget(info->header, &hb);
767 }
768 }
769
Init_MetaTableWidget(GLWidget * meta,list_t servers)770 static GLWidget *Init_MetaTableWidget(GLWidget *meta, list_t servers)
771 {
772 GLWidget *tmp, *row;
773 list_iter_t iter;
774 server_info_t *sip;
775 MetaTableWidget *info;
776 bool bg = true;
777
778 if (!(tmp = Init_EmptyBaseGLWidget())) {
779 error("Widget init failed");
780 return NULL;
781 }
782 if (!(info = (MetaTableWidget*)malloc(sizeof(MetaTableWidget)))) {
783 error("out of memory");
784 free(tmp);
785 return NULL;
786 }
787 info->meta = meta;
788 info->server_list = servers;
789 info->scrollbar = NULL;
790 info->header = NULL;
791 info->selected = NULL;
792 info->first_row = NULL;
793
794 tmp->wid_info = info;
795 tmp->WIDGET = METATABLEWIDGET;
796 tmp->SetBounds = SetBounds_MetaTableWidget;
797
798 for (iter = List_begin(servers);
799 iter != List_end(servers);
800 LI_FORWARD(iter)) {
801 sip = SI_DATA(iter);
802 row = Init_MetaRowWidget(sip, info, false, bg ? ROW_BG1 : ROW_BG2);
803 if (!row) break;
804 if (info->first_row == NULL)
805 info->first_row = (MetaRowWidget*)row->wid_info;
806 AppendGLWidgetList(&(tmp->children), row);
807 bg = !bg;
808 }
809 if ((info->header = Init_MetaHeaderWidget())) {
810 AppendGLWidgetList(&(tmp->children), info->header);
811 } else {
812 error("failed to create a header row for meta table");
813 }
814
815 return tmp;
816 }
817
Paint_MetaWidget(GLWidget * widget)818 static void Paint_MetaWidget(GLWidget *widget)
819 {
820 MetaWidget *info;
821 SDL_Rect *b;
822
823 if (widget->WIDGET != METAWIDGET) {
824 error("expected METAWIDGET got [%d]", widget->WIDGET);
825 return;
826 }
827 info = (MetaWidget*)widget->wid_info;
828 if (info->texture == 0) return;
829
830 b = &(widget->bounds);
831 glColor4ub(255, 255, 255, 255);
832 glBindTexture(GL_TEXTURE_2D, info->texture);
833 glEnable(GL_TEXTURE_2D);
834
835 glBegin(GL_QUADS);
836 glTexCoord2f(info->txc.MinX, info->txc.MinY);
837 glVertex2i(b->x, b->y);
838 glTexCoord2f(info->txc.MaxX, info->txc.MinY);
839 glVertex2i(b->x + b->w , b->y);
840 glTexCoord2f(info->txc.MaxX, info->txc.MaxY);
841 glVertex2i(b->x + b->w , b->y + b->h);
842 glTexCoord2f(info->txc.MinY, info->txc.MaxY);
843 glVertex2i(b->x, b->y + b->h);
844 glEnd();
845
846 glDisable(GL_TEXTURE_2D);
847 }
848
Close_MetaWidget(GLWidget * widget)849 static void Close_MetaWidget(GLWidget *widget)
850 {
851 MetaWidget *info;
852
853 if (widget->WIDGET != METAWIDGET) {
854 error("expected METAWIDGET got [%d]", widget->WIDGET);
855 return;
856 }
857 info = (MetaWidget*)widget->wid_info;
858 if (info->texture) glDeleteTextures(1, &(info->texture));
859 }
860
OnClick_Join(GLWidget * widget)861 static void OnClick_Join(GLWidget *widget)
862 {
863 SDL_Event evt;
864 evt.type = SDL_USEREVENT;
865 evt.user.code = EVENT_JOIN;
866 evt.user.data1 = NULL;
867 SDL_PushEvent(&evt);
868 }
869
OnClick_Refresh(GLWidget * widget)870 static void OnClick_Refresh(GLWidget *widget)
871 {
872 SDL_Event evt;
873 evt.type = SDL_USEREVENT;
874 evt.user.code = EVENT_REFRESH;
875 evt.user.data1 = NULL;
876 SDL_PushEvent(&evt);
877 }
878
OnClick_Quit(GLWidget * widget)879 static void OnClick_Quit(GLWidget *widget)
880 {
881 SDL_Event evt;
882 evt.type = SDL_QUIT;
883 SDL_PushEvent(&evt);
884 }
885
Init_MetaWidget(list_t servers)886 static GLWidget *Init_MetaWidget(list_t servers)
887 {
888 GLWidget *tmp;
889 MetaWidget *info;
890 MetaTableWidget *table;
891 SDL_Rect table_bounds;
892 SDL_Surface *surface;
893
894 if (!(tmp = Init_EmptyBaseGLWidget())) {
895 error("Widget init failed");
896 return NULL;
897 }
898 if (!(info = (MetaWidget*)malloc(sizeof(MetaWidget)))) {
899 error("out of memory");
900 free(tmp);
901 return NULL;
902 }
903 info->table = NULL;
904 info->status = NULL;
905 info->players = NULL;
906 info->texture = 0;
907 tmp->WIDGET = METAWIDGET;
908 tmp->bounds.x = (draw_width - META_WIDTH) / 2;
909 tmp->bounds.y = (draw_height - META_HEIGHT) / 2;
910 tmp->bounds.w = META_WIDTH;
911 tmp->bounds.h = META_HEIGHT;
912 tmp->wid_info = info;
913 tmp->Draw = Paint_MetaWidget;
914 tmp->Close = Close_MetaWidget;
915
916 if (!(info->table = Init_MetaTableWidget(tmp, servers))) {
917 free(tmp);
918 return NULL;
919 }
920 table = (MetaTableWidget*)info->table->wid_info;
921 table_bounds.x = tmp->bounds.x + 20;
922 table_bounds.y = tmp->bounds.y + 64;
923 table_bounds.w = 796;
924 table_bounds.h = 514;
925 SetBounds_GLWidget(info->table, &table_bounds);
926 AppendGLWidgetList(&(tmp->children), info->table);
927
928 #ifdef HAVE_SDL_IMAGE
929 surface = IMG_Load(CONF_TEXTUREDIR "sdlmetabg.png");
930 if (surface) {
931 info->texture = SDL_GL_LoadTexture(surface, &(info->txc));
932 SDL_FreeSurface(surface);
933 }
934 #endif
935
936 if (table->first_row != NULL)
937 SelectRow_MetaWidget(tmp, table->first_row);
938
939 return tmp;
940 }
941
join_server(Connect_param_t * conpar,server_info_t * sip)942 static bool join_server(Connect_param_t *conpar, server_info_t *sip)
943 {
944 char *server_addr_ptr = conpar->server_addr;
945 strlcpy(conpar->server_name, sip->hostname,
946 sizeof(conpar->server_name));
947 strlcpy(conpar->server_addr, sip->ip_str, sizeof(conpar->server_addr));
948 conpar->contact_port = sip->port;
949 if (Contact_servers(1, &server_addr_ptr, 1, 0, 0, NULL,
950 0, NULL, NULL, NULL, NULL, conpar)) {
951 return true;
952 }
953 printf("Server %s (%s) didn't respond on port %d\n",
954 conpar->server_name, conpar->server_addr,
955 conpar->contact_port);
956 return false;
957 }
958
handleKeyPress(GLWidget * meta,SDL_keysym * keysym)959 static void handleKeyPress(GLWidget *meta, SDL_keysym *keysym )
960 {
961 /*static unsigned int row = 1;*/
962 SDL_Event evt;
963
964 switch ( keysym->sym )
965 {
966 case SDLK_ESCAPE:
967 /* ESC key was pressed */
968 evt.type = SDL_QUIT;
969 SDL_PushEvent(&evt);
970 break;
971 case SDLK_F11:
972 /* F11 key was pressed
973 * this toggles fullscreen mode
974 */
975 #ifndef _WINDOWS
976 /* This segfaults */
977 /* SDL_WM_ToggleFullScreen(MainSDLSurface); */
978 #endif
979 break;
980 case SDLK_UP:
981 /* move the cursor up */
982 break;
983 case SDLK_DOWN:
984 /* move the curor down */
985 break;
986 default:
987 break;
988 }
989
990 return;
991 }
992
Meta_window(Connect_param_t * conpar)993 int Meta_window(Connect_param_t *conpar)
994 {
995 static char err[MSG_LEN] = {0};
996 int num_serv = 0;
997 int btn_x, btn_y, btn_h;
998 GLWidget *btn, *root, *meta, *target = NULL;
999 SDL_Event evt;
1000 server_info_t *server = NULL;
1001
1002 if (!server_list ||
1003 List_size(server_list) < 10 ||
1004 server_list_creation_time + 5 < time(NULL)) {
1005
1006 Delete_server_list();
1007 if ((num_serv = Get_meta_data(err)) <= 0) {
1008 error("Couldn't get meta list.");
1009 return -1;
1010 } else
1011 warn("Got %d servers.", num_serv);
1012 }
1013
1014 if (Welcome_sort_server_list() == -1) {
1015 Delete_server_list();
1016 error("out of memory");
1017 return -1;
1018 }
1019
1020 if (!(root = Init_EmptyBaseGLWidget())) {
1021 error("Widget init failed");
1022 return -1;
1023 }
1024 root->bounds.x = root->bounds.y = 0;
1025 root->bounds.w = draw_width;
1026 root->bounds.w = draw_height;
1027
1028 if (!(meta = Init_MetaWidget(server_list))) {
1029 free(root);
1030 return -1;
1031 }
1032 AppendGLWidgetList(&(root->children), meta);
1033
1034 btn_x = (draw_width - META_WIDTH) / 2 - 80;
1035 btn_y = (draw_height - META_HEIGHT) / 2 + 60;
1036 btn = Init_ImageButtonWidget("Join",
1037 "metabtnup.png",
1038 "metabtndown.png",
1039 BUTTON_BG,
1040 BUTTON_FG,
1041 OnClick_Join);
1042 if (btn) {
1043 btn->bounds.x = btn_x;
1044 btn->bounds.y = btn_y;
1045 btn_h = btn->bounds.h;
1046 btn_y += btn_h + 5;
1047 AppendGLWidgetList(&(root->children), btn);
1048 }
1049 btn = Init_ImageButtonWidget("Refresh",
1050 "metabtnup.png",
1051 "metabtndown.png",
1052 BUTTON_BG,
1053 BUTTON_FG,
1054 OnClick_Refresh);
1055 if (btn) {
1056 btn->bounds.x = btn_x;
1057 btn->bounds.y = btn_y;
1058 btn_h = btn->bounds.h;
1059 btn_y += btn_h + 5;
1060 AppendGLWidgetList(&(root->children), btn);
1061 }
1062 btn = Init_ImageButtonWidget("Quit",
1063 "metabtnup.png",
1064 "metabtndown.png",
1065 BUTTON_BG,
1066 BUTTON_FG,
1067 OnClick_Quit);
1068 if (btn) {
1069 btn->bounds.x = btn_x;
1070 btn->bounds.y = btn_y;
1071 btn_h = btn->bounds.h;
1072 btn_y += btn_h + 5;
1073 AppendGLWidgetList(&(root->children), btn);
1074 }
1075
1076 glMatrixMode(GL_MODELVIEW);
1077 glLoadIdentity();
1078 glMatrixMode(GL_PROJECTION);
1079 glLoadIdentity();
1080 gluOrtho2D(0, draw_width, draw_height, 0);
1081 glDisable(GL_BLEND);
1082
1083 while(1) {
1084
1085 set_alphacolor(blackRGBA);
1086 glBegin(GL_QUADS);
1087 glVertex2i(0,0);
1088 glVertex2i(draw_width,0);
1089 glVertex2i(draw_width,draw_height);
1090 glVertex2i(0,draw_height);
1091 glEnd();
1092 glEnable(GL_SCISSOR_TEST);
1093 DrawGLWidgetsi(meta, 0, 0, draw_width, draw_height);
1094 glDisable(GL_SCISSOR_TEST);
1095 SDL_GL_SwapBuffers();
1096
1097 SDL_WaitEvent(&evt);
1098 do {
1099
1100 switch(evt.type) {
1101 case SDL_QUIT:
1102 return -1;
1103
1104 case SDL_USEREVENT:
1105 if (evt.user.code == EVENT_JOIN) {
1106 server = (server_info_t*)evt.user.data1;
1107 if (server == NULL)
1108 server = GetSelectedServer_MetaWidget(meta);
1109 if (!server) break;
1110 if (join_server(conpar, server)) {
1111 Close_Widget(&root);
1112 glEnable(GL_BLEND);
1113 glMatrixMode(GL_PROJECTION);
1114 glLoadIdentity();
1115 gluOrtho2D(0, draw_width, 0, draw_height);
1116 return 0;
1117 }
1118 } else if (evt.user.code == EVENT_REFRESH) {
1119 Close_Widget(&root);
1120 return 1;
1121 }
1122 break;
1123
1124 case SDL_KEYDOWN:
1125 /* handle key presses */
1126 handleKeyPress( meta, &evt.key.keysym );
1127 break;
1128
1129 case SDL_MOUSEBUTTONDOWN:
1130 target = FindGLWidgeti(meta, evt.button.x, evt.button.y);
1131 if (target && target->button)
1132 target->button(evt.button.button,
1133 evt.button.state,
1134 evt.button.x,
1135 evt.button.y,
1136 target->buttondata);
1137 break;
1138
1139 case SDL_MOUSEBUTTONUP:
1140 if (target && target->button) {
1141 target->button(evt.button.button,
1142 evt.button.state,
1143 evt.button.x,
1144 evt.button.y,
1145 target->buttondata);
1146 target = NULL;
1147 }
1148 break;
1149
1150 case SDL_MOUSEMOTION:
1151 if (target && target->motion)
1152 target->motion(evt.motion.xrel,
1153 evt.motion.yrel,
1154 evt.button.x,
1155 evt.button.y,
1156 target->motiondata);
1157 break;
1158
1159 case SDL_VIDEOEXPOSE:
1160 glDisable(GL_SCISSOR_TEST);
1161 set_alphacolor(blackRGBA);
1162 glBegin(GL_QUADS);
1163 glVertex2i(0,0);
1164 glVertex2i(draw_width,0);
1165 glVertex2i(draw_width,draw_height);
1166 glVertex2i(0,draw_height);
1167 glEnd();
1168 glEnable(GL_SCISSOR_TEST);
1169 break;
1170 }
1171 } while (SDL_PollEvent(&evt));
1172 }
1173 }
1174