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