1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
13
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17
18 /* utility */
19 #include "log.h"
20 #include "support.h" /* bool */
21
22 /* common */
23 #include "city.h"
24 #include "connection.h"
25 #include "player.h"
26
27 /* client/include */
28 #include "canvas_g.h"
29 #include "citydlg_g.h"
30 #include "cityrep_g.h"
31 #include "dialogs_g.h"
32 #include "gui_main_g.h"
33 #include "menu_g.h"
34 #include "pages_g.h"
35 #include "plrdlg_g.h"
36 #include "ratesdlg_g.h"
37 #include "repodlgs_g.h"
38
39 /* client */
40 #include "client_main.h"
41 #include "connectdlg_common.h"
42 #include "options.h"
43 #include "tilespec.h"
44 #include "zoom.h"
45
46 #include "update_queue.h"
47
48
49 /* Data type in 'update_queue'. */
50 struct update_queue_data {
51 void *data;
52 uq_free_fn_t free_data_func;
53 };
54
55 static void update_queue_data_destroy(struct update_queue_data *pdata);
56
57 /* 'struct update_queue_hash' and related functions. */
58 #define SPECHASH_TAG update_queue
59 #define SPECHASH_IKEY_TYPE uq_callback_t
60 #define SPECHASH_IDATA_TYPE struct update_queue_data *
61 #define SPECHASH_IDATA_FREE update_queue_data_destroy
62 #include "spechash.h"
63 #define update_queue_hash_iterate(hash, callback, uq_data) \
64 TYPED_HASH_ITERATE(uq_callback_t, const struct update_queue_data *, \
65 hash, callback, uq_data)
66 #define update_queue_hash_iterate_end HASH_ITERATE_END
67
68 /* Type of data listed in 'processing_started_waiting_queue' and
69 * 'processing_finished_waiting_queue'. Real type is
70 * 'struct waiting_queue_list'. */
71 struct waiting_queue_data {
72 uq_callback_t callback;
73 struct update_queue_data *uq_data;
74 };
75
76 /* 'struct waiting_queue_list' and related functions. */
77 #define SPECLIST_TAG waiting_queue
78 #define SPECLIST_TYPE struct waiting_queue_data
79 #include "speclist.h"
80 #define waiting_queue_list_iterate(list, data) \
81 TYPED_LIST_ITERATE(struct waiting_queue_data, list, data)
82 #define waiting_queue_list_iterate_end LIST_ITERATE_END
83
84 /* 'struct waiting_queue_hash' and related functions. */
85 #define SPECHASH_TAG waiting_queue
86 #define SPECHASH_INT_KEY_TYPE
87 #define SPECHASH_IDATA_TYPE struct waiting_queue_list *
88 #define SPECHASH_IDATA_FREE waiting_queue_list_destroy
89 #include "spechash.h"
90
91 static struct update_queue_hash *update_queue = NULL;
92 static struct waiting_queue_hash *processing_started_waiting_queue = NULL;
93 static struct waiting_queue_hash *processing_finished_waiting_queue = NULL;
94 static int update_queue_frozen_level = 0;
95 static bool update_queue_has_idle_callback = FALSE;
96
97 static void update_unqueue(void *data);
98 static inline void update_queue_push(uq_callback_t callback,
99 struct update_queue_data *uq_data);
100
101 /****************************************************************************
102 Create a new update queue data.
103 ****************************************************************************/
104 static inline struct update_queue_data *
update_queue_data_new(void * data,uq_free_fn_t free_data_func)105 update_queue_data_new(void *data, uq_free_fn_t free_data_func)
106 {
107 struct update_queue_data *uq_data = fc_malloc(sizeof(*uq_data));
108
109 uq_data->data = data;
110 uq_data->free_data_func = free_data_func;
111 return uq_data;
112 }
113
114 /****************************************************************************
115 Free a update queue data.
116 ****************************************************************************/
update_queue_data_destroy(struct update_queue_data * uq_data)117 static void update_queue_data_destroy(struct update_queue_data *uq_data)
118 {
119 fc_assert_ret(NULL != uq_data);
120 if (NULL != uq_data->free_data_func) {
121 uq_data->free_data_func(uq_data->data);
122 }
123 free(uq_data);
124 }
125
126 /****************************************************************************
127 Create a new waiting queue data.
128 ****************************************************************************/
129 static inline struct waiting_queue_data *
waiting_queue_data_new(uq_callback_t callback,void * data,uq_free_fn_t free_data_func)130 waiting_queue_data_new(uq_callback_t callback, void *data,
131 uq_free_fn_t free_data_func)
132 {
133 struct waiting_queue_data *wq_data = fc_malloc(sizeof(*wq_data));
134
135 wq_data->callback = callback;
136 wq_data->uq_data = update_queue_data_new(data, free_data_func);
137 return wq_data;
138 }
139
140 /****************************************************************************
141 Free a waiting queue data.
142 ****************************************************************************/
waiting_queue_data_destroy(struct waiting_queue_data * wq_data)143 static void waiting_queue_data_destroy(struct waiting_queue_data *wq_data)
144 {
145 fc_assert_ret(NULL != wq_data);
146 if (NULL != wq_data->uq_data) {
147 /* May be NULL, see waiting_queue_data_extract(). */
148 update_queue_data_destroy(wq_data->uq_data);
149 }
150 free(wq_data);
151 }
152
153 /****************************************************************************
154 Extract the update_queue_data from the waiting queue data.
155 ****************************************************************************/
156 static inline struct update_queue_data *
waiting_queue_data_extract(struct waiting_queue_data * wq_data)157 waiting_queue_data_extract(struct waiting_queue_data *wq_data)
158 {
159 struct update_queue_data *uq_data = wq_data->uq_data;
160
161 wq_data->uq_data = NULL;
162 return uq_data;
163 }
164
165
166 /****************************************************************************
167 Initialize the update queue.
168 ****************************************************************************/
update_queue_init(void)169 void update_queue_init(void)
170 {
171 if (NULL != update_queue) {
172 /* Already initialized. */
173 fc_assert(NULL != processing_started_waiting_queue);
174 fc_assert(NULL != processing_finished_waiting_queue);
175 return;
176 }
177 fc_assert(NULL == processing_started_waiting_queue);
178 fc_assert(NULL == processing_finished_waiting_queue);
179
180 update_queue = update_queue_hash_new();
181 processing_started_waiting_queue = waiting_queue_hash_new();
182 processing_finished_waiting_queue = waiting_queue_hash_new();
183 update_queue_frozen_level = 0;
184 update_queue_has_idle_callback = FALSE;
185 }
186
187 /****************************************************************************
188 Free the update queue.
189 ****************************************************************************/
update_queue_free(void)190 void update_queue_free(void)
191 {
192 fc_assert(NULL != update_queue);
193 fc_assert(NULL != processing_started_waiting_queue);
194 fc_assert(NULL != processing_finished_waiting_queue);
195
196 if (NULL != update_queue) {
197 update_queue_hash_destroy(update_queue);
198 update_queue = NULL;
199 }
200 if (NULL != processing_started_waiting_queue) {
201 waiting_queue_hash_destroy(processing_started_waiting_queue);
202 processing_started_waiting_queue = NULL;
203 }
204 if (NULL != processing_finished_waiting_queue) {
205 waiting_queue_hash_destroy(processing_finished_waiting_queue);
206 processing_finished_waiting_queue = NULL;
207 }
208
209 update_queue_frozen_level = 0;
210 update_queue_has_idle_callback = FALSE;
211 }
212
213 /****************************************************************************
214 Freezes the update queue.
215 ****************************************************************************/
update_queue_freeze(void)216 void update_queue_freeze(void)
217 {
218 update_queue_frozen_level++;
219 }
220
221 /****************************************************************************
222 Free the update queue.
223 ****************************************************************************/
update_queue_thaw(void)224 void update_queue_thaw(void)
225 {
226 update_queue_frozen_level--;
227 if (0 == update_queue_frozen_level
228 && !update_queue_has_idle_callback
229 && NULL != update_queue
230 && 0 < update_queue_hash_size(update_queue)) {
231 update_queue_has_idle_callback = TRUE;
232 add_idle_callback(update_unqueue, NULL);
233 } else if (0 > update_queue_frozen_level) {
234 log_error("update_queue_frozen_level < 0, repairing...");
235 update_queue_frozen_level = 0;
236 }
237 }
238
239 /****************************************************************************
240 Free the update queue.
241 ****************************************************************************/
update_queue_force_thaw(void)242 void update_queue_force_thaw(void)
243 {
244 while (update_queue_is_frozen()) {
245 update_queue_thaw();
246 }
247 }
248
249 /****************************************************************************
250 Free the update queue.
251 ****************************************************************************/
update_queue_is_frozen(void)252 bool update_queue_is_frozen(void)
253 {
254 return (0 < update_queue_frozen_level);
255 }
256
257 /****************************************************************************
258 Moves the instances waiting to the request_id to the callback queue.
259 ****************************************************************************/
260 static inline void
waiting_queue_execute_pending_requests(struct waiting_queue_hash * hash,int request_id)261 waiting_queue_execute_pending_requests(struct waiting_queue_hash *hash,
262 int request_id)
263 {
264 struct waiting_queue_list *list;
265
266 if (NULL == hash || !waiting_queue_hash_lookup(hash, request_id, &list)) {
267 return;
268 }
269
270 waiting_queue_list_iterate(list, wq_data) {
271 update_queue_push(wq_data->callback, waiting_queue_data_extract(wq_data));
272 } waiting_queue_list_iterate_end;
273 waiting_queue_hash_remove(hash, request_id);
274 }
275
276 /****************************************************************************
277 Moves the instances waiting to the request_id to the callback queue.
278 ****************************************************************************/
update_queue_processing_started(int request_id)279 void update_queue_processing_started(int request_id)
280 {
281 waiting_queue_execute_pending_requests(processing_started_waiting_queue,
282 request_id);
283 }
284
285 /****************************************************************************
286 Moves the instances waiting to the request_id to the callback queue.
287 ****************************************************************************/
update_queue_processing_finished(int request_id)288 void update_queue_processing_finished(int request_id)
289 {
290 waiting_queue_execute_pending_requests(processing_finished_waiting_queue,
291 request_id);
292 }
293
294 /****************************************************************************
295 Unqueue all updates.
296 ****************************************************************************/
update_unqueue(void * data)297 static void update_unqueue(void *data)
298 {
299 struct update_queue_hash *hash;
300
301 if (NULL == update_queue) {
302 update_queue_has_idle_callback = FALSE;
303 return;
304 }
305
306 if (update_queue_is_frozen() || !tileset_is_fully_loaded()) {
307 /* Cannot update now, let's add it again. */
308 update_queue_has_idle_callback = FALSE;
309 return;
310 }
311
312 /* Replace the old list, don't do recursive stuff, and don't write in the
313 * hash table when we are reading it. */
314 hash = update_queue;
315 update_queue = update_queue_hash_new();
316 update_queue_has_idle_callback = FALSE;
317
318 /* Invoke callbacks. */
319 update_queue_hash_iterate(hash, callback, uq_data) {
320 callback(uq_data->data);
321 } update_queue_hash_iterate_end;
322 update_queue_hash_destroy(hash);
323 }
324
325 /****************************************************************************
326 Add a callback to the update queue. NB: you can only set a callback
327 once. Setting a callback twice will overwrite the previous.
328 ****************************************************************************/
update_queue_push(uq_callback_t callback,struct update_queue_data * uq_data)329 static inline void update_queue_push(uq_callback_t callback,
330 struct update_queue_data *uq_data)
331 {
332 update_queue_hash_replace(update_queue, callback, uq_data);
333
334 if (!update_queue_has_idle_callback
335 && !update_queue_is_frozen()) {
336 update_queue_has_idle_callback = TRUE;
337 add_idle_callback(update_unqueue, NULL);
338 }
339 }
340
341 /****************************************************************************
342 Add a callback to the update queue. NB: you can only set a callback
343 once. Setting a callback twice will overwrite the previous.
344 ****************************************************************************/
update_queue_add(uq_callback_t callback,void * data)345 void update_queue_add(uq_callback_t callback, void *data)
346 {
347 if (NULL != update_queue) {
348 update_queue_push(callback, update_queue_data_new(data, NULL));
349 }
350 }
351
352 /****************************************************************************
353 Add a callback to the update queue. NB: you can only set a callback
354 once. Setting a callback twice will overwrite the previous.
355 ****************************************************************************/
update_queue_add_full(uq_callback_t callback,void * data,uq_free_fn_t free_data_func)356 void update_queue_add_full(uq_callback_t callback, void *data,
357 uq_free_fn_t free_data_func)
358 {
359 if (NULL != update_queue) {
360 update_queue_push(callback, update_queue_data_new(data, free_data_func));
361 }
362 }
363
364 /****************************************************************************
365 Returns whether this callback is listed in the update queue.
366 ****************************************************************************/
update_queue_has_callback(uq_callback_t callback)367 bool update_queue_has_callback(uq_callback_t callback)
368 {
369 return (NULL != update_queue
370 && update_queue_hash_lookup(update_queue, callback, NULL));
371 }
372
373 /****************************************************************************
374 Returns whether this callback is listed in the update queue and
375 get the its data and free function. 'data' and 'free_data_func' can
376 be NULL.
377 ****************************************************************************/
update_queue_has_callback_full(uq_callback_t callback,const void ** data,uq_free_fn_t * free_data_func)378 bool update_queue_has_callback_full(uq_callback_t callback,
379 const void **data,
380 uq_free_fn_t *free_data_func)
381 {
382 if (NULL != update_queue) {
383 struct update_queue_data *uq_data;
384
385 if (update_queue_hash_lookup(update_queue, callback, &uq_data)) {
386 if (NULL != data) {
387 *data = uq_data->data;
388 }
389 if (NULL != free_data_func) {
390 *free_data_func = uq_data->free_data_func;
391 }
392 return TRUE;
393 }
394 }
395 return FALSE;
396 }
397
398 /****************************************************************************
399 Connects the callback to a network event.
400 ****************************************************************************/
401 static inline void
waiting_queue_add_pending_request(struct waiting_queue_hash * hash,int request_id,uq_callback_t callback,void * data,uq_free_fn_t free_data_func)402 waiting_queue_add_pending_request(struct waiting_queue_hash *hash,
403 int request_id, uq_callback_t callback,
404 void *data, uq_free_fn_t free_data_func)
405 {
406 if (NULL != hash) {
407 struct waiting_queue_list *list;
408
409 if (!waiting_queue_hash_lookup(hash, request_id, &list)) {
410 /* The list doesn't exist yet for that request, create it. */
411 list = waiting_queue_list_new_full(waiting_queue_data_destroy);
412 waiting_queue_hash_insert(hash, request_id, list);
413 }
414 waiting_queue_list_append(list, waiting_queue_data_new(callback, data,
415 free_data_func));
416 }
417 }
418
419 /****************************************************************************
420 Connects the callback to the start of the processing (in server side) of
421 the request.
422 ****************************************************************************/
update_queue_connect_processing_started(int request_id,uq_callback_t callback,void * data)423 void update_queue_connect_processing_started(int request_id,
424 uq_callback_t callback,
425 void *data)
426 {
427 waiting_queue_add_pending_request(processing_started_waiting_queue,
428 request_id, callback, data, NULL);
429 }
430
431 /****************************************************************************
432 Connects the callback to the start of the processing (in server side) of
433 the request.
434 ****************************************************************************/
update_queue_connect_processing_started_full(int request_id,uq_callback_t callback,void * data,uq_free_fn_t free_data_func)435 void update_queue_connect_processing_started_full(int request_id,
436 uq_callback_t callback,
437 void *data,
438 uq_free_fn_t
439 free_data_func)
440 {
441 waiting_queue_add_pending_request(processing_started_waiting_queue,
442 request_id, callback, data,
443 free_data_func);
444 }
445
446 /****************************************************************************
447 Connects the callback to the end of the processing (in server side) of
448 the request.
449 ****************************************************************************/
update_queue_connect_processing_finished(int request_id,uq_callback_t callback,void * data)450 void update_queue_connect_processing_finished(int request_id,
451 uq_callback_t callback,
452 void *data)
453 {
454 waiting_queue_add_pending_request(processing_finished_waiting_queue,
455 request_id, callback, data, NULL);
456 }
457
458 /****************************************************************************
459 Connects the callback to the end of the processing (in server side) of
460 the request.
461 ****************************************************************************/
update_queue_connect_processing_finished_full(int request_id,uq_callback_t callback,void * data,uq_free_fn_t free_data_func)462 void update_queue_connect_processing_finished_full(int request_id,
463 uq_callback_t callback,
464 void *data,
465 uq_free_fn_t
466 free_data_func)
467 {
468 waiting_queue_add_pending_request(processing_finished_waiting_queue,
469 request_id, callback, data,
470 free_data_func);
471 }
472
473
474 /****************************************************************************
475 Set the client page.
476 ****************************************************************************/
set_client_page_callback(void * data)477 static void set_client_page_callback(void *data)
478 {
479 enum client_pages page = FC_PTR_TO_INT(data);
480
481 real_set_client_page(page);
482
483 if (page == PAGE_GAME) {
484 if (has_zoom_support()) {
485 if (gui_options.zoom_set) {
486 zoom_set(gui_options.zoom_default_level);
487 } else {
488 zoom_1_0();
489 }
490 }
491 }
492 }
493
494 /****************************************************************************
495 Set the client page.
496 ****************************************************************************/
set_client_page(enum client_pages page)497 void set_client_page(enum client_pages page)
498 {
499 log_debug("Requested page: %s.", client_pages_name(page));
500
501 update_queue_add(set_client_page_callback, FC_INT_TO_PTR(page));
502 }
503
504 /****************************************************************************
505 Start server and then, set the client page.
506 ****************************************************************************/
client_start_server_and_set_page(enum client_pages page)507 void client_start_server_and_set_page(enum client_pages page)
508 {
509 log_debug("Requested server start + page: %s.", client_pages_name(page));
510
511 if (client_start_server()) {
512 update_queue_connect_processing_finished(client.conn.client.last_request_id_used,
513 set_client_page_callback,
514 FC_INT_TO_PTR(page));
515 }
516 }
517
518 /****************************************************************************
519 Returns the next client page.
520 ****************************************************************************/
get_client_page(void)521 enum client_pages get_client_page(void)
522 {
523 const void *data;
524
525 if (update_queue_has_callback_full(set_client_page_callback,
526 &data, NULL)) {
527 return FC_PTR_TO_INT(data);
528 } else {
529 return get_current_client_page();
530 }
531 }
532
533 /****************************************************************************
534 Returns whether there's page switching already in progress.
535 ****************************************************************************/
update_queue_is_switching_page(void)536 bool update_queue_is_switching_page(void)
537 {
538 return update_queue_has_callback(set_client_page_callback);
539 }
540
541 /****************************************************************************
542 Update the menus.
543 ****************************************************************************/
menus_update_callback(void * data)544 static void menus_update_callback(void *data)
545 {
546 if (FC_PTR_TO_INT(data)) {
547 real_menus_init();
548 }
549 real_menus_update();
550 }
551
552 /****************************************************************************
553 Request the menus to be initialized and updated.
554 ****************************************************************************/
menus_init(void)555 void menus_init(void)
556 {
557 update_queue_add(menus_update_callback, FC_INT_TO_PTR(TRUE));
558 }
559
560 /****************************************************************************
561 Request the menus to be updated.
562 ****************************************************************************/
menus_update(void)563 void menus_update(void)
564 {
565 if (!update_queue_has_callback(menus_update_callback)) {
566 update_queue_add(menus_update_callback, FC_INT_TO_PTR(FALSE));
567 }
568 }
569
570 /****************************************************************************
571 Update multipliers/policy dialog.
572 ****************************************************************************/
multipliers_dialog_update(void)573 void multipliers_dialog_update(void)
574 {
575 update_queue_add(real_multipliers_dialog_update, NULL);
576 }
577
578 /****************************************************************************
579 Update cities gui.
580 ****************************************************************************/
cities_update_callback(void * data)581 static void cities_update_callback(void *data)
582 {
583 #ifdef FREECIV_DEBUG
584 #define NEED_UPDATE(city_update, action) \
585 if (city_update & need_update) { \
586 action; \
587 need_update &= ~city_update; \
588 }
589 #else /* FREECIV_DEBUG */
590 #define NEED_UPDATE(city_update, action) \
591 if (city_update & need_update) { \
592 action; \
593 }
594 #endif /* FREECIV_DEBUG */
595
596 cities_iterate(pcity) {
597 enum city_updates need_update = pcity->client.need_updates;
598
599 if (CU_NO_UPDATE == need_update) {
600 continue;
601 }
602
603 /* Clear all updates. */
604 pcity->client.need_updates = CU_NO_UPDATE;
605
606 NEED_UPDATE(CU_UPDATE_REPORT, real_city_report_update_city(pcity));
607 NEED_UPDATE(CU_UPDATE_DIALOG, real_city_dialog_refresh(pcity));
608 NEED_UPDATE(CU_POPUP_DIALOG, real_city_dialog_popup(pcity));
609
610 #ifdef FREECIV_DEBUG
611 if (CU_NO_UPDATE != need_update) {
612 log_error("Some city updates not handled "
613 "for city %s (id %d): %d left.",
614 city_name_get(pcity), pcity->id, need_update);
615 }
616 #endif /* FREECIV_DEBUG */
617 } cities_iterate_end;
618 #undef NEED_UPDATE
619 }
620
621 /****************************************************************************
622 Request the city dialog to be popped up for the city.
623 ****************************************************************************/
popup_city_dialog(struct city * pcity)624 void popup_city_dialog(struct city *pcity)
625 {
626 pcity->client.need_updates |= CU_POPUP_DIALOG;
627 update_queue_add(cities_update_callback, NULL);
628 }
629
630 /****************************************************************************
631 Request the city dialog to be updated for the city.
632 ****************************************************************************/
refresh_city_dialog(struct city * pcity)633 void refresh_city_dialog(struct city *pcity)
634 {
635 pcity->client.need_updates |= CU_UPDATE_DIALOG;
636 update_queue_add(cities_update_callback, NULL);
637 }
638
639 /****************************************************************************
640 Request the city to be updated in the city report.
641 ****************************************************************************/
city_report_dialog_update_city(struct city * pcity)642 void city_report_dialog_update_city(struct city *pcity)
643 {
644 pcity->client.need_updates |= CU_UPDATE_REPORT;
645 update_queue_add(cities_update_callback, NULL);
646 }
647
648
649 /****************************************************************************
650 Update the connection list in the start page.
651 ****************************************************************************/
conn_list_dialog_update(void)652 void conn_list_dialog_update(void)
653 {
654 update_queue_add(real_conn_list_dialog_update, NULL);
655 }
656
657
658 /****************************************************************************
659 Update the nation report.
660 ****************************************************************************/
players_dialog_update(void)661 void players_dialog_update(void)
662 {
663 update_queue_add(real_players_dialog_update, NULL);
664 }
665
666 /****************************************************************************
667 Update the city report.
668 ****************************************************************************/
city_report_dialog_update(void)669 void city_report_dialog_update(void)
670 {
671 update_queue_add(real_city_report_dialog_update, NULL);
672 }
673
674 /****************************************************************************
675 Update the science report.
676 ****************************************************************************/
science_report_dialog_update(void)677 void science_report_dialog_update(void)
678 {
679 update_queue_add(real_science_report_dialog_update, NULL);
680 }
681
682 /****************************************************************************
683 Update the economy report.
684 ****************************************************************************/
economy_report_dialog_update(void)685 void economy_report_dialog_update(void)
686 {
687 update_queue_add(real_economy_report_dialog_update, NULL);
688 }
689
690 /****************************************************************************
691 Update the units report.
692 ****************************************************************************/
units_report_dialog_update(void)693 void units_report_dialog_update(void)
694 {
695 update_queue_add(real_units_report_dialog_update, NULL);
696 }
697
698 /****************************************************************************
699 Update the units report.
700 ****************************************************************************/
unit_select_dialog_update(void)701 void unit_select_dialog_update(void)
702 {
703 update_queue_add(unit_select_dialog_update_real, NULL);
704 }
705