Atrinik Client  4.0
network_graph.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
32 #include <global.h>
33 #include <network_graph.h>
34 #include <toolkit/string.h>
35 
36 #ifndef __CPROTO__
37 
41 typedef struct network_graph_data {
42  size_t *data;
43  int width;
44  int pos;
45  Uint32 ticks;
46  size_t max;
48 
52 typedef struct network_graph_widget {
57 
61  int type;
62 
66  uint32_t filters;
68 
72 typedef struct network_graph_work {
74  int type;
75  int traffic;
76  size_t bytes;
78 
82 static const char *const network_graph_types[NETWORK_GRAPH_TYPE_MAX] = {
83  "Game data", "HTTP data"
84 };
85 
89 static const char *const network_graph_filters[NETWORK_GRAPH_TRAFFIC_MAX] = {
90  "Received", "Transmitted"
91 };
92 
96 static const char *const network_graph_colors[NETWORK_GRAPH_TYPE_MAX] = {
97  "#ff0000", "#00ff00"
98 };
99 
103 static SDL_mutex *network_graph_mutex = NULL;
104 
109 
110 /* Prototypes */
111 static void widget_network_graph_update(widgetdata *widget, int type,
112  int traffic, size_t bytes);
113 
115 static void widget_draw(widgetdata *widget)
116 {
117  if (!widget->redraw) {
118  return;
119  }
120 
121  network_graph_widget_t *network_graph = widget->subwidget;
122  network_graph_data_t *data = &network_graph->data[network_graph->type];
123 
124  SDL_FillRect(widget->surface, NULL, 0);
125 
126  if (data->data == NULL) {
127  return;
128  }
129 
130  for (int i = 0; i < NETWORK_GRAPH_TRAFFIC_MAX; i++) {
131  if (!BIT_QUERY(network_graph->filters, i)) {
132  continue;
133  }
134 
135  SDL_Color color;
136  if (!text_color_parse(network_graph_colors[i], &color)) {
137  LOG(ERROR, "Could not parse color: %s",
139  continue;
140  }
141 
142  int ly = widget->h - 1;
143  for (int x = 0; x < data->pos && x < widget->w; x++) {
144  size_t bytes = data->data[NETWORK_GRAPH_TRAFFIC_MAX * x + i];
145  long double factor;
146  if (data->max == 0) {
147  factor = 0.0;
148  } else {
149  factor = bytes / (long double) data->max;
150  }
151  int y = (widget->h - 1) - (widget->h - 1) * factor;
152 
153  lineRGBA(widget->surface, MAX(0, x - 1), ly, x, y, color.r,
154  color.g, color.b, 255);
155  ly = y;
156  }
157  }
158 }
159 
161 static void widget_background(widgetdata *widget, int draw)
162 {
163  if (!widget->show) {
164  if (network_graph_mutex != NULL) {
165  SDL_DestroyMutex(network_graph_mutex);
166  network_graph_mutex = NULL;
167  }
168 
169  return;
170  }
171 
172  if (network_graph_mutex == NULL) {
173  network_graph_mutex = SDL_CreateMutex();
174  }
175 
176  network_graph_widget_t *network_graph = widget->subwidget;
177 
178  SDL_LockMutex(network_graph_mutex);
179  while (work_queue != NULL) {
181 
182  for (widgetdata *tmp = cur_widget[NETWORK_GRAPH_ID]; tmp != NULL;
183  tmp = tmp->type_next) {
184  widget_network_graph_update(tmp, work->type, work->traffic,
185  work->bytes);
186  }
187 
188  LL_DELETE(work_queue, work);
189  efree(work);
190  }
191  SDL_UnlockMutex(network_graph_mutex);
192 
193  for (int type = 0; type < NETWORK_GRAPH_TYPE_MAX; type++) {
194  network_graph_data_t *data = &network_graph->data[type];
195  if (LastTick - data->ticks <= 1000) {
196  continue;
197  }
198 
199  for (int traffic = 0; traffic < NETWORK_GRAPH_TRAFFIC_MAX; traffic++) {
201  }
202  }
203 }
204 
206 static int widget_event(widgetdata *widget, SDL_Event *event)
207 {
208  network_graph_widget_t *network_graph = widget->subwidget;
209  network_graph_data_t *data = &network_graph->data[network_graph->type];
210 
211  if (event->type == SDL_MOUSEMOTION) {
212  int x = event->motion.x - widget->x;
213  if (x < 0 || x >= widget->w) {
214  return 0;
215  }
216 
217  char buf[HUGE_BUF];
218  snprintf(VS(buf), "Maximum: %" PRIu64 " Bytes/s (%" PRIu64 " kB/s)",
219  (uint64_t) data->max, (uint64_t) data->max / 1000);
220 
221  for (int i = 0; i < NETWORK_GRAPH_TRAFFIC_MAX; i++) {
222  size_t bytes = data->data[x * NETWORK_GRAPH_TRAFFIC_MAX + i];
223  snprintfcat(VS(buf), "\n%s: %" PRIu64 " Bytes/s (%" PRIu64 " kB/s)",
224  network_graph_filters[i], (uint64_t) bytes,
225  (uint64_t) bytes / 1000);
226  }
227 
228  tooltip_create(event->motion.x, event->motion.y, FONT_ARIAL11, buf);
229  tooltip_multiline(200);
231  }
232 
233  return 0;
234 }
235 
237 static void widget_deinit(widgetdata *widget)
238 {
239  network_graph_widget_t *network_graph = widget->subwidget;
240 
241  for (int i = 0; i < NETWORK_GRAPH_TYPE_MAX; i++) {
242  if (network_graph->data[i].data != NULL) {
243  efree(network_graph->data[i].data);
244  }
245  }
246 
247  if (network_graph_mutex != NULL) {
248  SDL_LockMutex(network_graph_mutex);
249  while (work_queue != NULL) {
251  LL_DELETE(work_queue, work);
252  efree(work);
253  }
254  SDL_UnlockMutex(network_graph_mutex);
255 
256  SDL_DestroyMutex(network_graph_mutex);
257  network_graph_mutex = NULL;
258  }
259 }
260 
261 static void menu_network_graph_display_change(widgetdata *widget,
262  widgetdata *menuitem, SDL_Event *event)
263 {
264  network_graph_widget_t *network_graph = widget->subwidget;
265 
266  for (widgetdata *tmp = menuitem->inv; tmp != NULL; tmp = tmp->next) {
267  if (tmp->type == LABEL_ID) {
268  _widget_label *label = LABEL(tmp);
269 
270  for (int i = 0; i < NETWORK_GRAPH_TYPE_MAX; i++) {
271  if (strcmp(network_graph_types[i], label->text) == 0) {
272  network_graph->type = i;
273  widget->redraw = 1;
274  break;
275  }
276  }
277 
278  break;
279  }
280  }
281 }
282 
283 static void menu_network_graph_display(widgetdata *widget, widgetdata *menuitem,
284  SDL_Event *event)
285 {
286  network_graph_widget_t *network_graph = widget->subwidget;
287  widgetdata *submenu = MENU(menuitem->env)->submenu;
288 
289  for (int i = 0; i < NETWORK_GRAPH_TYPE_MAX; i++) {
290  add_menuitem(submenu, network_graph_types[i],
291  &menu_network_graph_display_change, MENU_RADIO,
292  network_graph->type == i);
293  }
294 }
295 
296 static void menu_network_graph_filters_change(widgetdata *widget,
297  widgetdata *menuitem, SDL_Event *event)
298 {
299  network_graph_widget_t *network_graph = widget->subwidget;
300 
301  for (widgetdata *tmp = menuitem->inv; tmp != NULL; tmp = tmp->next) {
302  if (tmp->type == LABEL_ID) {
303  _widget_label *label = LABEL(tmp);
304 
305  for (int i = 0; i < NETWORK_GRAPH_TRAFFIC_MAX; i++) {
306  if (strcmp(network_graph_filters[i], label->text) == 0) {
307  BIT_FLIP(network_graph->filters, i);
308  widget->redraw = 1;
309  break;
310  }
311  }
312 
313  break;
314  }
315  }
316 }
317 
318 static void menu_network_graph_filters(widgetdata *widget, widgetdata *menuitem,
319  SDL_Event *event)
320 {
321  network_graph_widget_t *network_graph = widget->subwidget;
322  widgetdata *submenu = MENU(menuitem->env)->submenu;
323 
324  for (int i = 0; i < NETWORK_GRAPH_TRAFFIC_MAX; i++) {
326  &menu_network_graph_filters_change, MENU_CHECKBOX,
327  BIT_QUERY(network_graph->filters, i));
328  }
329 }
330 
332 static int widget_menu_handle(widgetdata *widget, SDL_Event *event)
333 {
334  widgetdata *menu = create_menu(event->motion.x, event->motion.y, widget);
335  widget_menu_standard_items(widget, menu);
336  add_menuitem(menu, "Display >", &menu_network_graph_display, MENU_SUBMENU,
337  0);
338  add_menuitem(menu, "Filters >", &menu_network_graph_filters, MENU_SUBMENU,
339  0);
340  menu_finalize(menu);
341  return 1;
342 }
343 
355 static void widget_network_graph_update(widgetdata *widget, int type,
356  int traffic, size_t bytes)
357 {
358  HARD_ASSERT(widget != NULL);
359 
360  if (!widget->show) {
361  return;
362  }
363 
364  network_graph_widget_t *network_graph = widget->subwidget;
365  network_graph_data_t *data = &network_graph->data[type];
366 
367  if (data->data == NULL || data->width != widget->w) {
368  if (data->data == NULL) {
369  data->ticks = LastTick;
370  }
371 
372  data->data = ereallocz(data->data,
373  sizeof(*data->data) * NETWORK_GRAPH_TRAFFIC_MAX * data->width,
374  sizeof(*data->data) * NETWORK_GRAPH_TRAFFIC_MAX * widget->w);
375  data->width = widget->w;
376  }
377 
378  if (data->width == 0) {
379  return;
380  }
381 
382  if (LastTick - data->ticks > 1000) {
383  data->pos++;
384  data->ticks = LastTick;
385  }
386 
387  if (data->pos == data->width) {
388  data->pos--;
389 
390  bool recalc_max = false;
391  for (int i = 0; i < NETWORK_GRAPH_TRAFFIC_MAX; i++) {
392  if (data->data[i] >= data->max) {
393  recalc_max = true;
394  }
395  }
396 
397  memmove(data->data, data->data + NETWORK_GRAPH_TRAFFIC_MAX,
398  sizeof(*data->data) * (NETWORK_GRAPH_TRAFFIC_MAX *
399  (data->width - 1)));
400 
401  if (recalc_max) {
402  data->max = 0;
403  }
404 
405  for (int i = 0; i < NETWORK_GRAPH_TRAFFIC_MAX; i++) {
406  data->data[data->pos * NETWORK_GRAPH_TRAFFIC_MAX + i] = 0;
407 
408  if (recalc_max) {
409  for (int x = 0; x < data->pos; x++) {
410  size_t bytes2 = data->data[x * NETWORK_GRAPH_TRAFFIC_MAX +
411  i];
412  if (bytes2 > data->max) {
413  data->max = bytes2;
414  }
415  }
416  }
417  }
418  }
419 
420  size_t *dst = &data->data[data->pos * NETWORK_GRAPH_TRAFFIC_MAX + traffic];
421  *dst += bytes;
422 
423  if (*dst > data->max) {
424  data->max = *dst;
425  }
426 
427  widget->redraw = 1;
428 }
429 
440 void network_graph_update(int type, int traffic, size_t bytes)
441 {
442  HARD_ASSERT(type >= 0 && type < NETWORK_GRAPH_TYPE_MAX);
443  HARD_ASSERT(traffic >= 0 && traffic < NETWORK_GRAPH_TRAFFIC_MAX);
444 
445  if (network_graph_mutex == NULL) {
446  return;
447  }
448 
449  network_graph_work_t *work = ecalloc(1, sizeof(*work));
450  work->type = type;
451  work->traffic = traffic;
452  work->bytes = bytes;
453 
454  SDL_LockMutex(network_graph_mutex);
455  LL_PREPEND(work_queue, work);
456  SDL_UnlockMutex(network_graph_mutex);
457 }
458 
465 {
466  network_graph_widget_t *network_graph = ecalloc(1, sizeof(*network_graph));
467  network_graph->filters = ~0U;
468 
469  widget->draw_func = widget_draw;
470  widget->background_func = widget_background;
471  widget->event_func = widget_event;
472  widget->deinit_func = widget_deinit;
473  widget->menu_handle_func = widget_menu_handle;
474  widget->subwidget = network_graph;
475 }
476 
477 #endif
int text_color_parse(const char *color_notation, SDL_Color *color)
Definition: text.c:603
static int widget_menu_handle(widgetdata *widget, SDL_Event *event)
struct widgetdata * env
Definition: widget.h:98
Uint32 ticks
When the data was last logged.
Definition: network_graph.c:45
struct network_graph_work * next
Next entry.
Definition: network_graph.c:73
struct network_graph_data network_graph_data_t
static void widget_draw(widgetdata *widget)
network_graph_data_t data[NETWORK_GRAPH_TYPE_MAX]
Definition: network_graph.c:56
void * subwidget
Definition: widget.h:107
static int widget_event(widgetdata *widget, SDL_Event *event)
SDL_Surface * surface
Definition: widget.h:110
void network_graph_update(int type, int traffic, size_t bytes)
static network_graph_work_t * work_queue
int traffic
Traffic type.
Definition: network_graph.c:75
size_t max
Peak.
Definition: network_graph.c:46
static const char *const network_graph_types[NETWORK_GRAPH_TYPE_MAX]
Definition: network_graph.c:82
void menu_finalize(widgetdata *widget)
Definition: widget.c:2524
void tooltip_create(int mx, int my, font_struct *font, const char *text)
Definition: tooltip.c:60
struct widgetdata * type_next
Definition: widget.h:101
struct widgetdata * next
Definition: widget.h:86
struct widgetdata * inv
Definition: widget.h:92
static void widget_network_graph_update(widgetdata *widget, int type, int traffic, size_t bytes)
static const char *const network_graph_colors[NETWORK_GRAPH_TYPE_MAX]
Definition: network_graph.c:96
static const char *const network_graph_filters[NETWORK_GRAPH_TRAFFIC_MAX]
Definition: network_graph.c:89
uint8_t show
Definition: widget.h:69
void widget_network_graph_init(widgetdata *widget)
widgetdata * cur_widget[TOTAL_SUBWIDGETS]
Definition: widget.c:75
uint8_t redraw
Definition: widget.h:72
Number of traffic types.
Definition: network_graph.h:46
struct network_graph_work network_graph_work_t
int x
Definition: widget.h:48
Maximum number of data types.
Definition: network_graph.h:39
void tooltip_enable_delay(uint32_t delay)
Definition: tooltip.c:82
static void widget_deinit(widgetdata *widget)
widgetdata * create_menu(int x, int y, widgetdata *owner)
Definition: widget.c:2428
size_t bytes
Bytes.
Definition: network_graph.c:76
int w
Definition: widget.h:54
int type
Data type.
Definition: network_graph.c:74
int h
Definition: widget.h:57
void tooltip_multiline(int max_width)
Definition: tooltip.c:96
uint32_t LastTick
Definition: main.c:57
size_t * data
The actual data.
Definition: network_graph.c:42
int pos
Position in the data array.
Definition: network_graph.c:44
int width
Number of entries in the data array.
Definition: network_graph.c:43
static void widget_background(widgetdata *widget, int draw)
static SDL_mutex * network_graph_mutex
void add_menuitem(widgetdata *menu, const char *text, void(*menu_func_ptr)(widgetdata *, widgetdata *, SDL_Event *event), int menu_type, int val)
Definition: widget.c:2451
struct network_graph_widget network_graph_widget_t
char * text
Definition: widget.h:210