554 lines
13 KiB
C
554 lines
13 KiB
C
/* XQF - Quake server browser and launcher
|
|
* Copyright (C) 1998-2000 Roman Pozlevich <roma@botik.ru>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
*/
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "utils.h"
|
|
#include "pixmaps.h"
|
|
#include "game.h"
|
|
#include "loadpixmap.h"
|
|
|
|
|
|
// hack to make dlsym work
|
|
#define static
|
|
|
|
#include ICONS_C_INCLUDE
|
|
|
|
#include "xpm/update.xpm"
|
|
#include "xpm/refresh.xpm"
|
|
#include "xpm/refrsel.xpm"
|
|
#include "xpm/stop.xpm"
|
|
|
|
#include "xpm/connect.xpm"
|
|
#include "xpm/observe.xpm"
|
|
#include "xpm/record.xpm"
|
|
|
|
#include "xpm/sfilter.xpm"
|
|
#include "xpm/sfilter-cfg.xpm"
|
|
#include "xpm/pfilter.xpm"
|
|
#include "xpm/pfilter-cfg.xpm"
|
|
|
|
#include "xpm/green-plus.xpm"
|
|
#include "xpm/red-minus.xpm"
|
|
|
|
#include "xpm/man-black.xpm"
|
|
#include "xpm/man-red.xpm"
|
|
#include "xpm/man-yellow.xpm"
|
|
|
|
#include "xpm/group-red.xpm"
|
|
#include "xpm/group-green.xpm"
|
|
#include "xpm/group-blue.xpm"
|
|
|
|
#include "xpm/buddy-red.xpm"
|
|
#include "xpm/buddy-green.xpm"
|
|
#include "xpm/buddy-blue.xpm"
|
|
|
|
#include "xpm/error.xpm"
|
|
#include "xpm/delete.xpm"
|
|
|
|
#include "xpm/server-na.xpm"
|
|
#include "xpm/server-up.xpm"
|
|
#include "xpm/server-down.xpm"
|
|
#include "xpm/server-to.xpm"
|
|
#include "xpm/server-error.xpm"
|
|
#include "xpm/locked.xpm"
|
|
#include "xpm/punkbuster.xpm"
|
|
#include "xpm/locked_punkbuster.xpm"
|
|
#undef static
|
|
|
|
struct pixmap update_pix;
|
|
struct pixmap refresh_pix;
|
|
struct pixmap refrsel_pix;
|
|
struct pixmap stop_pix;
|
|
|
|
struct pixmap connect_pix;
|
|
struct pixmap observe_pix;
|
|
struct pixmap record_pix;
|
|
|
|
struct pixmap sfilter_pix;
|
|
struct pixmap sfilter_cfg_pix;
|
|
struct pixmap pfilter_pix;
|
|
struct pixmap pfilter_cfg_pix;
|
|
|
|
struct pixmap gplus_pix;
|
|
struct pixmap rminus_pix;
|
|
|
|
struct pixmap man_black_pix;
|
|
struct pixmap man_red_pix;
|
|
struct pixmap man_yellow_pix;
|
|
|
|
struct pixmap group_pix[3];
|
|
struct pixmap buddy_pix[9];
|
|
|
|
struct pixmap error_pix;
|
|
struct pixmap delete_pix;
|
|
|
|
struct pixmap server_status[5];
|
|
struct pixmap locked_pix;
|
|
struct pixmap punkbuster_pix;
|
|
struct pixmap locked_punkbuster_pix;
|
|
|
|
static GdkGC *pixmaps_gc;
|
|
static GdkGC *masks_gc;
|
|
static GdkColor mask_pattern;
|
|
|
|
|
|
int pixmap_height (GdkPixmap *pixmap) {
|
|
int height, width;
|
|
|
|
if (!pixmap)
|
|
return 0;
|
|
|
|
gdk_window_get_size (pixmap, &width, &height);
|
|
return height;
|
|
}
|
|
|
|
|
|
int pixmap_width (GdkPixmap *pixmap) {
|
|
int height, width;
|
|
|
|
if (!pixmap)
|
|
return 0;
|
|
|
|
gdk_window_get_size (pixmap, &width, &height);
|
|
return width;
|
|
}
|
|
|
|
|
|
void free_pixmap (struct pixmap *pixmap) {
|
|
if (!pixmap)
|
|
return;
|
|
|
|
if (pixmap->pix) {
|
|
gdk_pixmap_unref (pixmap->pix);
|
|
pixmap->pix = NULL;
|
|
}
|
|
if (pixmap->mask) {
|
|
gdk_bitmap_unref (pixmap->mask);
|
|
pixmap->mask = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static void create_pixmap (GtkWidget *widget, const char* file, struct pixmap *pix) {
|
|
load_pixmap_as_pixmap(widget, file, pix);
|
|
|
|
if (!pix->pix) {
|
|
pix->pix = error_pix.pix;
|
|
pix->mask = error_pix.mask;
|
|
gdk_pixmap_ref(pix->pix);
|
|
gdk_bitmap_ref(pix->mask);
|
|
}
|
|
}
|
|
|
|
void free_pixmaps (void) {
|
|
unsigned i;
|
|
|
|
free_pixmap (&update_pix);
|
|
free_pixmap (&refresh_pix);
|
|
free_pixmap (&refrsel_pix);
|
|
free_pixmap (&stop_pix);
|
|
|
|
free_pixmap (&connect_pix);
|
|
free_pixmap (&observe_pix);
|
|
free_pixmap (&record_pix);
|
|
|
|
free_pixmap (&sfilter_pix);
|
|
free_pixmap (&sfilter_cfg_pix);
|
|
free_pixmap (&pfilter_pix);
|
|
free_pixmap (&pfilter_cfg_pix);
|
|
|
|
free_pixmap (&gplus_pix);
|
|
free_pixmap (&rminus_pix);
|
|
|
|
free_pixmap (&man_black_pix);
|
|
free_pixmap (&man_red_pix);
|
|
free_pixmap (&man_yellow_pix); // He's actually green
|
|
|
|
for (i = 0; i < 3; i++)
|
|
free_pixmap (&group_pix[i]);
|
|
|
|
for (i = 0; i < 9; i++)
|
|
free_pixmap (&buddy_pix[i]);
|
|
|
|
free_pixmap (&error_pix);
|
|
free_pixmap (&delete_pix);
|
|
|
|
for (i = 0; i < 5; i++)
|
|
free_pixmap (&server_status[i]);
|
|
|
|
free_pixmap (&locked_pix);
|
|
|
|
free_pixmap (&punkbuster_pix);
|
|
free_pixmap (&locked_punkbuster_pix);
|
|
|
|
for (i = LAN_SERVER; i < UNKNOWN_SERVER; i++) {
|
|
free_pixmap(games[i].pix);
|
|
g_free(games[i].pix);
|
|
games[i].pix = NULL;
|
|
}
|
|
|
|
if (pixmaps_gc) {
|
|
gdk_gc_destroy (pixmaps_gc);
|
|
pixmaps_gc = NULL;
|
|
}
|
|
|
|
if (masks_gc) {
|
|
gdk_gc_destroy (masks_gc);
|
|
masks_gc = NULL;
|
|
}
|
|
}
|
|
|
|
/** \brief concatenate two pixmaps
|
|
*
|
|
* horizontal concatenation
|
|
* @param window
|
|
* @param dest destination pixmap
|
|
* @param s1 first pixmap
|
|
* @param s2 second pixmap
|
|
* @returns dest for convenience
|
|
*/
|
|
struct pixmap* cat_pixmaps (GtkWidget *window, struct pixmap *dest, struct pixmap* s1, struct pixmap* s2) {
|
|
GdkGC *white_gc;
|
|
int h1, w1, h2, w2;
|
|
|
|
if (!GTK_WIDGET_REALIZED (window))
|
|
gtk_widget_realize (window);
|
|
|
|
gdk_window_get_size (s1->pix, &w1, &h1);
|
|
gdk_window_get_size (s2->pix, &w2, &h2);
|
|
|
|
dest->pix = gdk_pixmap_new (window->window, w1 + w2, MAX (h1, h2), -1);
|
|
dest->mask = gdk_pixmap_new (window->window, w1 + w2, MAX (h1, h2), 1);
|
|
|
|
white_gc = window->style->base_gc[GTK_STATE_NORMAL];
|
|
|
|
if (!masks_gc) {
|
|
masks_gc = gdk_gc_new (dest->mask);
|
|
gdk_gc_set_exposures (masks_gc, FALSE);
|
|
}
|
|
|
|
mask_pattern.pixel = 0;
|
|
gdk_gc_set_foreground (masks_gc, &mask_pattern);
|
|
gdk_draw_rectangle (dest->mask, masks_gc, TRUE, 0, 0, -1, -1);
|
|
|
|
mask_pattern.pixel = 1;
|
|
gdk_gc_set_foreground (masks_gc, &mask_pattern);
|
|
|
|
gdk_gc_set_clip_origin (white_gc, 0, 0);
|
|
gdk_gc_set_clip_mask (white_gc, s1->mask);
|
|
gdk_draw_pixmap (dest->pix, white_gc, s1->pix, 0, 0, 0, 0, w1, h1);
|
|
gdk_draw_pixmap (dest->mask, masks_gc, s1->mask, 0, 0, 0, 0, w1, h1);
|
|
|
|
gdk_gc_set_clip_origin (white_gc, w1, 0);
|
|
gdk_gc_set_clip_mask (white_gc, s2->mask);
|
|
gdk_draw_pixmap (dest->pix, white_gc, s2->pix, 0, 0, w1, 0, w2, h2);
|
|
gdk_draw_pixmap (dest->mask, masks_gc, s2->mask, 0, 0, w1, 0, w2, h2);
|
|
|
|
gdk_gc_set_clip_origin (white_gc, 0, 0);
|
|
gdk_gc_set_clip_mask (white_gc, NULL);
|
|
|
|
return dest;
|
|
}
|
|
|
|
|
|
void init_pixmaps (GtkWidget *window) {
|
|
unsigned i = 0;
|
|
|
|
free_pixmaps ();
|
|
|
|
if (!GTK_WIDGET_REALIZED (window))
|
|
gtk_widget_realize (window);
|
|
|
|
create_pixmap (window, "update.xpm", &update_pix);
|
|
create_pixmap (window, "refresh.xpm", &refresh_pix);
|
|
create_pixmap (window, "refrsel.xpm", &refrsel_pix);
|
|
create_pixmap (window, "stop.xpm", &stop_pix);
|
|
|
|
create_pixmap (window, "connect.xpm", &connect_pix);
|
|
create_pixmap (window, "observe.xpm", &observe_pix);
|
|
create_pixmap (window, "record.xpm", &record_pix);
|
|
|
|
create_pixmap (window, "sfilter.xpm", &sfilter_pix);
|
|
create_pixmap (window, "sfilter-cfg.xpm", &sfilter_cfg_pix);
|
|
|
|
create_pixmap (window, "pfilter.xpm", &pfilter_pix);
|
|
create_pixmap (window, "pfilter-cfg.xpm", &pfilter_cfg_pix);
|
|
|
|
create_pixmap (window, "green-plus.xpm", &gplus_pix);
|
|
create_pixmap (window, "red-minus.xpm", &rminus_pix);
|
|
|
|
create_pixmap (window, "man-black.xpm", &man_black_pix);
|
|
create_pixmap (window, "man-red.xpm", &man_red_pix);
|
|
create_pixmap (window, "man-yellow.xpm", &man_yellow_pix);
|
|
|
|
create_pixmap (window, "group-red.xpm", &group_pix[0]);
|
|
create_pixmap (window, "group-green.xpm", &group_pix[1]);
|
|
create_pixmap (window, "group-blue.xpm", &group_pix[2]);
|
|
|
|
create_pixmap (window, "buddy-red.xpm", &buddy_pix[1]);
|
|
create_pixmap (window, "buddy-green.xpm", &buddy_pix[2]);
|
|
create_pixmap (window, "buddy-blue.xpm", &buddy_pix[4]);
|
|
|
|
create_pixmap (window, "server-na.xpm", &server_status[0]);
|
|
create_pixmap (window, "server-up.xpm", &server_status[1]);
|
|
create_pixmap (window, "server-down.xpm", &server_status[2]);
|
|
create_pixmap (window, "server-to.xpm", &server_status[3]);
|
|
create_pixmap (window, "server-error.xpm", &server_status[4]);
|
|
|
|
create_pixmap (window, "error.xpm", &error_pix);
|
|
create_pixmap (window, "delete.xpm", &delete_pix);
|
|
create_pixmap (window, "locked.xpm", &locked_pix);
|
|
create_pixmap (window, "punkbuster.xpm", &punkbuster_pix);
|
|
cat_pixmaps(window, &locked_punkbuster_pix, &punkbuster_pix, &locked_pix);
|
|
|
|
for (i = 0; i < UNKNOWN_SERVER; i++) {
|
|
struct pixmap* pix = NULL;
|
|
|
|
pix = g_malloc0(sizeof(struct pixmap));
|
|
|
|
if (games[i].icon) {
|
|
create_pixmap(window, games[i].icon, pix);
|
|
}
|
|
|
|
games[i].pix = pix;
|
|
}
|
|
}
|
|
|
|
|
|
void ensure_buddy_pix (GtkWidget *window, int n) {
|
|
int width, height;
|
|
GdkGC *white_gc;
|
|
int pri;
|
|
int sec;
|
|
|
|
if (!buddy_pix[1].pix) /* not initialized */
|
|
return;
|
|
|
|
if (n < 0 || n > 9 || buddy_pix[n].pix)
|
|
return;
|
|
|
|
sec = ((n & 0x04) != 0)? 0x04 : 0x02;
|
|
pri = n & ~sec;
|
|
|
|
ensure_buddy_pix (window, pri);
|
|
|
|
if (!pri || !sec)
|
|
return;
|
|
|
|
if (!GTK_WIDGET_REALIZED (window))
|
|
gtk_widget_realize (window);
|
|
|
|
gdk_window_get_size (buddy_pix[1].pix, &width, &height);
|
|
|
|
buddy_pix[n].pix = gdk_pixmap_new (window->window, width, height, -1);
|
|
buddy_pix[n].mask = gdk_pixmap_new (window->window, width, height, 1);
|
|
|
|
white_gc = window->style->white_gc;
|
|
|
|
if (!masks_gc) {
|
|
masks_gc = gdk_gc_new (buddy_pix[n].mask);
|
|
gdk_gc_set_exposures (masks_gc, FALSE);
|
|
}
|
|
|
|
gdk_gc_set_foreground (masks_gc, &window->style->white);
|
|
|
|
gdk_draw_pixmap (buddy_pix[n].pix, white_gc, buddy_pix[pri].pix,
|
|
0, 0, 0, 0, width, height);
|
|
gdk_draw_pixmap (buddy_pix[n].mask, masks_gc, buddy_pix[pri].mask,
|
|
0, 0, 0, 0, width, height);
|
|
|
|
gdk_gc_set_clip_mask (white_gc, buddy_pix[sec].mask);
|
|
gdk_draw_pixmap (buddy_pix[n].pix, white_gc, buddy_pix[sec].pix,
|
|
0, 0, 0, 0, width, height);
|
|
gdk_gc_set_clip_mask (white_gc, NULL);
|
|
|
|
gdk_gc_set_clip_mask (masks_gc, buddy_pix[sec].mask);
|
|
gdk_draw_rectangle (buddy_pix[n].mask, masks_gc, TRUE, 0, 0, width, height);
|
|
gdk_gc_set_clip_mask (masks_gc, NULL);
|
|
}
|
|
|
|
|
|
GdkPixmap *two_colors_pixmap (GdkWindow *window, int width, int height,
|
|
GdkColor *top, GdkColor *bottom) {
|
|
GdkPixmap *pixmap;
|
|
|
|
pixmap = gdk_pixmap_new (window, width, height, -1);
|
|
|
|
if (!pixmaps_gc)
|
|
pixmaps_gc = gdk_gc_new (window);
|
|
|
|
gdk_gc_set_foreground (pixmaps_gc, top);
|
|
gdk_draw_rectangle (pixmap, pixmaps_gc, TRUE, 0, 0, width, height/2);
|
|
|
|
gdk_gc_set_foreground (pixmaps_gc, bottom);
|
|
gdk_draw_rectangle (pixmap, pixmaps_gc, TRUE, 0, height/2, width,
|
|
height - height/2);
|
|
return pixmap;
|
|
}
|
|
|
|
|
|
void create_server_pixmap (GtkWidget *window, struct pixmap *stype,
|
|
int n, GdkPixmap **pix, GdkBitmap **mask) {
|
|
GdkGC *white_gc;
|
|
int hb, wb, hs, ws;
|
|
|
|
if (!GTK_WIDGET_REALIZED (window))
|
|
gtk_widget_realize (window);
|
|
|
|
gdk_window_get_size (buddy_pix[1].pix, &wb, &hb);
|
|
gdk_window_get_size (stype->pix, &ws, &hs);
|
|
|
|
*pix = gdk_pixmap_new (window->window, wb + ws, MAX (hs, hb), -1);
|
|
*mask = gdk_pixmap_new (window->window, wb + ws, MAX (hs, hb), 1);
|
|
|
|
white_gc = window->style->base_gc[GTK_STATE_NORMAL];
|
|
|
|
if (!masks_gc) {
|
|
masks_gc = gdk_gc_new (*mask);
|
|
gdk_gc_set_exposures (masks_gc, FALSE);
|
|
}
|
|
|
|
mask_pattern.pixel = 0;
|
|
gdk_gc_set_foreground (masks_gc, &mask_pattern);
|
|
gdk_draw_rectangle (*mask, masks_gc, TRUE, 0, 0, -1, -1);
|
|
|
|
mask_pattern.pixel = 1;
|
|
gdk_gc_set_foreground (masks_gc, &mask_pattern);
|
|
|
|
if (n) {
|
|
ensure_buddy_pix (window, n);
|
|
|
|
gdk_gc_set_clip_origin (white_gc, 0, 0);
|
|
gdk_gc_set_clip_mask (white_gc, buddy_pix[n].mask);
|
|
gdk_draw_pixmap (*pix, white_gc, buddy_pix[n].pix, 0, 0, 0, 0, wb, hb);
|
|
|
|
gdk_draw_pixmap (*mask, masks_gc, buddy_pix[n].mask, 0, 0, 0, 0, wb, hb);
|
|
}
|
|
|
|
gdk_gc_set_clip_origin (white_gc, wb, 0);
|
|
gdk_gc_set_clip_mask (white_gc, stype->mask);
|
|
gdk_draw_pixmap (*pix, white_gc, stype->pix, 0, 0, wb, 0, ws, hs);
|
|
|
|
gdk_draw_pixmap (*mask, masks_gc, stype->mask, 0, 0, wb, 0, ws, hs);
|
|
|
|
gdk_gc_set_clip_origin (white_gc, 0, 0);
|
|
gdk_gc_set_clip_mask (white_gc, NULL);
|
|
}
|
|
|
|
|
|
void pixmap_cache_lookup (GSList *cache, GdkPixmap **pix, GdkBitmap **mask,
|
|
unsigned key) {
|
|
struct cached_pixmap *cp;
|
|
GdkPixmap *res_pix = NULL;
|
|
GdkBitmap *res_mask = NULL;
|
|
|
|
if (!pix)
|
|
return;
|
|
|
|
while (cache) {
|
|
cp = (struct cached_pixmap *) cache->data;
|
|
if (cp->key == key) {
|
|
cp->weight += 2;
|
|
res_pix = cp->pix;
|
|
res_mask = cp->mask;
|
|
break;
|
|
}
|
|
cache = cache->next;
|
|
}
|
|
|
|
*pix = res_pix;
|
|
if (res_pix)
|
|
gdk_pixmap_ref (res_pix);
|
|
|
|
if (mask) {
|
|
*mask = res_mask;
|
|
if (res_mask)
|
|
gdk_bitmap_ref (res_mask);
|
|
}
|
|
}
|
|
|
|
|
|
void pixmap_cache_add (GSList **cache, GdkPixmap *pix, GdkBitmap *mask,
|
|
unsigned key) {
|
|
struct cached_pixmap *cp;
|
|
|
|
if (pix && cache) {
|
|
cp = g_malloc0 (sizeof (struct cached_pixmap));
|
|
|
|
cp->pix = pix;
|
|
gdk_pixmap_ref (pix);
|
|
|
|
if (mask) {
|
|
cp->mask = mask;
|
|
gdk_bitmap_ref (mask);
|
|
}
|
|
|
|
cp->key = key;
|
|
cp->weight = 10;
|
|
|
|
*cache = g_slist_prepend (*cache, cp);
|
|
}
|
|
}
|
|
|
|
|
|
static int cached_pixmap_cmp (const struct cached_pixmap *a,
|
|
const struct cached_pixmap *b) {
|
|
return b->weight - a->weight; /* descending order */
|
|
}
|
|
|
|
|
|
static void free_cached_pixmap (struct cached_pixmap *cp) {
|
|
|
|
gdk_pixmap_unref (cp->pix);
|
|
|
|
if (cp->mask)
|
|
gdk_bitmap_unref (cp->mask);
|
|
|
|
g_free (cp);
|
|
}
|
|
|
|
|
|
void pixmap_cache_clear (GSList **cache, int maxitems) {
|
|
GSList *tmp = NULL;
|
|
GSList *last = NULL;
|
|
struct cached_pixmap *cp;
|
|
|
|
if (cache && *cache) {
|
|
if (maxitems > 0) {
|
|
*cache = g_slist_sort (*cache, (GCompareFunc) cached_pixmap_cmp);
|
|
|
|
for (tmp = *cache; (maxitems-- > 0) && tmp; tmp = tmp->next) {
|
|
cp = (struct cached_pixmap *) tmp->data;
|
|
cp->weight /= 2;
|
|
last = tmp;
|
|
}
|
|
|
|
if (last)
|
|
last->next = NULL;
|
|
}
|
|
else {
|
|
tmp = *cache;
|
|
*cache = NULL;
|
|
}
|
|
|
|
g_slist_foreach (tmp, (GFunc) free_cached_pixmap, NULL);
|
|
g_slist_free (tmp);
|
|
}
|
|
}
|