ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/pclient
Revision: 1.50
Committed: Sun Apr 9 21:05:50 2006 UTC (18 years, 2 months ago) by root
Branch: MAIN
Changes since 1.49: +13 -8 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 #!/opt/bin/perl
2 root 1.25
3 root 1.2 use strict;
4 root 1.25 use utf8;
5 root 1.2
6 root 1.13 use Glib;
7     use Gtk2 -init;
8    
9     use SDL;
10     use SDL::App;
11     use SDL::Event;
12     use SDL::Surface;
13     use SDL::OpenGL;
14     use SDL::OpenGL::Constants;
15    
16 elmex 1.11 use Crossfire;
17 root 1.2 use Crossfire::Client;
18     use Crossfire::Protocol;
19    
20 elmex 1.20 use Crossfire::Client::Widget;
21 elmex 1.10
22 root 1.19 our $FACECACHE;
23    
24 root 1.13 our $VERSION = '0.1';
25 root 1.2
26 root 1.30 our %GL_EXT;
27    
28 elmex 1.10 our $CFG;
29 root 1.13 our $CONN;
30 root 1.2
31 root 1.30 our $WIDTH;
32     our $HEIGHT;
33     our $FULLSCREEN;
34    
35 root 1.39 our $FONTSIZE;
36 root 1.24
37 root 1.13 our $SDL_TIMER;
38     our $SDL_APP;
39     our $SDL_EV = new SDL::Event;
40     our %SDL_CB;
41 root 1.18
42     our @GL_INIT; # hooks called on every gl init
43 root 1.2
44 root 1.30 our $ALT_ENTER_MESSAGE;
45    
46 elmex 1.41 our $TOPLEVEL;
47    
48 elmex 1.34 our $tw; # Test widget #d#
49 elmex 1.32
50 root 1.45 my $last_refresh;
51 root 1.50 my %ANIMATE;
52     my $refresh_handler;
53 root 1.45
54 root 1.13 sub init_screen {
55     $SDL_APP = new SDL::App
56 root 1.5 -flags => SDL_ANYFORMAT | SDL_HWSURFACE,
57     -title => "Crossfire+ Client",
58 root 1.30 -width => $WIDTH,
59     -height => $HEIGHT,
60 root 1.5 -opengl => 1,
61     -red_size => 8,
62     -green_size => 8,
63     -blue_size => 8,
64 root 1.2 -double_buffer => 1,
65 root 1.30 -fullscreen => $FULLSCREEN,
66 root 1.5 -resizeable => 0;
67 root 1.2
68 root 1.45 $last_refresh = SDL::GetTicks;
69    
70 root 1.30 %GL_EXT = map +($_ => 1), split /\s+/, Crossfire::Client::gl_extensions;
71    
72     $GL_EXT{GL_ARB_texture_non_power_of_two}
73     or warn "WARNING: non-power-of-two opengl extension required";
74    
75 root 1.40 $FONTSIZE = int $HEIGHT / 50;
76 root 1.39
77 root 1.40 $ALT_ENTER_MESSAGE = new Crossfire::Client::Widget::Label
78     0, $FONTSIZE, $HEIGHT - $FONTSIZE, $FONTSIZE,
79     "Use <b>Alt-Enter</b> to toggle fullscreen mode";
80 root 1.30 $ALT_ENTER_MESSAGE->move (0, $HEIGHT - ($ALT_ENTER_MESSAGE->size_request)[1]);
81 elmex 1.41 $TOPLEVEL->add ($ALT_ENTER_MESSAGE);
82 root 1.30
83 elmex 1.34 # Test code #d#
84 elmex 1.43 unless ($tw) { # haha...
85 root 1.45 $tw = new Crossfire::Client::Widget::Animator;
86 elmex 1.46 my $lbl1 = new Crossfire::Client::Widget::Label
87     0, 0, 10, $FONTSIZE, "<i>This</i> is a\n<u>TEST</u>!\nOf a themed\nFrame!";
88     my $lbl2 = new Crossfire::Client::Widget::Label
89     0, 0, 10, $FONTSIZE, "LBL2";
90    
91     my $vb = new Crossfire::Client::Widget::VBox;
92     my $f = new Crossfire::Client::Widget::FancyFrame;
93     my $f2 = new Crossfire::Client::Widget::FancyFrame;
94     $f->add ($lbl1);
95     $f2->add ($lbl2);
96     $vb->add ($f);
97     $vb->add ($f2, 1);
98    
99     $tw->add ($vb);
100     $tw->w (400);
101     $tw->h (300);
102 elmex 1.44 $tw->move ($WIDTH - 200, 0);
103 root 1.45 $tw->moveto (0, 0);
104 elmex 1.41 $TOPLEVEL->add ($tw);
105 elmex 1.46
106     # $f->move ($WIDTH - 200, 0);
107     # $TOPLEVEL->add ($f);
108 root 1.39 }
109 elmex 1.32
110 root 1.19 glClearColor 0, 0, 0, 0;
111    
112 root 1.2 glEnable GL_TEXTURE_2D;
113 elmex 1.36 glEnable GL_COLOR_MATERIAL;
114 root 1.2 glShadeModel GL_FLAT;
115     glDisable GL_DEPTH_TEST;
116 root 1.9 glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
117 root 1.2
118 root 1.17 $_->() for @GL_INIT;
119 root 1.2 }
120    
121 root 1.13 sub start_game {
122 root 1.50 $SDL_TIMER = add Glib::Timeout 1000/50, sub {
123 root 1.30 ($SDL_CB{$SDL_EV->type} || sub { warn "unhandled event ", $SDL_EV->type })->()
124     while $SDL_EV->poll;
125 root 1.13
126     1
127     };
128    
129 root 1.30 $WIDTH = $CFG->{width};
130     $HEIGHT = $CFG->{height};
131     $FULLSCREEN = 0;
132    
133 root 1.13 init_screen;
134    
135 root 1.50 my $mapsize = List::Util::min 64, List::Util::max 11, int $HEIGHT * $CFG->{mapsize} * 0.01 / 32;
136    
137 root 1.13 $CONN = new conn
138     host => $CFG->{host},
139 root 1.28 port => $CFG->{port},
140     user => $CFG->{user},
141 root 1.37 pass => $CFG->{password},
142 root 1.50 mapw => $mapsize,
143     maph => $mapsize,
144 root 1.37 ;
145 root 1.45
146     Crossfire::Client::lowdelay fileno $CONN->{fh};
147 root 1.13 }
148    
149     sub stop_game {
150     remove Glib::Source $SDL_TIMER;
151 root 1.50 remove Glib::Source $refresh_handler;
152     undef $refresh_handler;
153 root 1.13
154     undef $SDL_APP;
155 root 1.50 undef $CONN;
156 root 1.13 SDL::Quit;
157     }
158    
159 root 1.30
160     sub force_refresh {
161     glViewport 0, 0, $WIDTH, $HEIGHT;
162 root 1.35
163     glMatrixMode GL_PROJECTION;
164     glLoadIdentity;
165 root 1.45 glOrtho 0, $WIDTH, $HEIGHT, 0, -6000 , 6000;
166 root 1.35 glMatrixMode GL_MODELVIEW;
167    
168 root 1.5 glClear GL_COLOR_BUFFER_BIT;
169 root 1.2
170 elmex 1.41 $TOPLEVEL->draw;
171 root 1.1
172 root 1.2 SDL::GLSwapBuffers;
173 root 1.1 }
174    
175 root 1.30 sub refresh {
176     $refresh_handler ||= add Glib::Idle sub {
177 root 1.50 return unless $SDL_APP;
178    
179 root 1.45 my $next_refresh = SDL::GetTicks;
180     my $interval = ($next_refresh - $last_refresh) * 0.001;
181     $last_refresh = $next_refresh;
182    
183 root 1.30 force_refresh;
184 root 1.45 $_->animate ($interval) for grep $_, values %ANIMATE;
185    
186     if (%ANIMATE) {
187     1
188     } else {
189     undef $refresh_handler;
190     0
191     }
192 root 1.30 };
193     }
194    
195 root 1.45 sub animation_start {
196     my ($widget) = @_;
197     $ANIMATE{$widget} = $widget;
198     Scalar::Util::weaken $ANIMATE{$widget};
199    
200     refresh;
201     }
202    
203     sub animation_stop {
204     my ($widget) = @_;
205     delete $ANIMATE{$widget};
206     }
207    
208 root 1.13 %SDL_CB = (
209     SDL_QUIT() => sub {
210 root 1.19 main_quit Gtk2;
211 root 1.13 },
212     SDL_VIDEORESIZE() => sub {
213     },
214     SDL_VIDEOEXPOSE() => sub {
215     refresh;
216     },
217     SDL_KEYDOWN() => sub {
218 root 1.18 if ($SDL_EV->key_mod & KMOD_ALT && $SDL_EV->key_sym == SDLK_RETURN) {
219     # alt-enter
220 root 1.30 $FULLSCREEN = !$FULLSCREEN;
221 root 1.18 init_screen;
222     } else {
223 root 1.22 Crossfire::Client::Widget::feed_sdl_key_down_event ($SDL_EV);
224 root 1.18 }
225 root 1.13 },
226     SDL_KEYUP() => sub {
227 root 1.22 Crossfire::Client::Widget::feed_sdl_key_up_event ($SDL_EV);
228 root 1.13 },
229     SDL_MOUSEMOTION() => sub {
230     warn "sdl motion\n";#d#
231     },
232     SDL_MOUSEBUTTONDOWN() => sub {
233 root 1.22 Crossfire::Client::Widget::feed_sdl_button_down_event ($SDL_EV);
234 root 1.13 },
235     SDL_MOUSEBUTTONUP() => sub {
236 root 1.22 Crossfire::Client::Widget::feed_sdl_button_up_event ($SDL_EV);
237 root 1.13 },
238     SDL_ACTIVEEVENT() => sub {
239     warn "active\n";#d#
240     },
241     );
242 root 1.1
243 root 1.2 @conn::ISA = Crossfire::Protocol::;
244 root 1.1
245 root 1.2 sub conn::map_update {
246 root 1.1 my ($self, $dirty) = @_;
247    
248 root 1.2 refresh;
249 root 1.1 }
250    
251 root 1.2 sub conn::map_scroll {
252 root 1.1 my ($self, $dx, $dy) = @_;
253    
254 root 1.45 # refresh;
255 root 1.1 }
256    
257 root 1.2 sub conn::map_clear {
258 root 1.1 my ($self) = @_;
259    
260 root 1.45 # refresh;
261 root 1.1 }
262    
263 root 1.19 sub conn::face_find {
264     my ($self, $face) = @_;
265    
266     $FACECACHE->{"$face->{chksum},$face->{name}"}
267     }
268    
269 root 1.2 sub conn::face_update {
270 root 1.19 my ($self, $face) = @_;
271    
272     $FACECACHE->{"$face->{chksum},$face->{name}"} = $face->{image};
273 root 1.1
274 root 1.18 $face->{texture} = new_from_image Crossfire::Client::Texture delete $face->{image};
275 root 1.1 }
276    
277 root 1.33 sub conn::query {
278     my ($self, $flags, $prompt) = @_;
279    
280     warn "<<<<QUERY:$flags:$prompt>>>\n";#d#
281     }
282    
283 elmex 1.23 sub gtk_add_cfg_field {
284     my ($tbl, $cfg, $klbl, $key, $value) = @_;
285     my $i = $cfg->{_i}++;
286     $tbl->attach_defaults (my $lbl = Gtk2::Label->new ($klbl), 0, 1, $i, $i + 1);
287     $tbl->attach_defaults (my $ent = Gtk2::Entry->new, 1, 2, $i, $i + 1);
288     if ($key eq 'password') {
289     $ent->set_invisible_char ("*");
290     $ent->set (visibility => 0)
291     }
292     $ent->set_text ($value);
293     $ent->signal_connect (changed => sub {
294     my ($ent) = @_;
295     $cfg->{$key} = $ent->get_text;
296 elmex 1.49 # TODO: Mapsize should be a slider in the game gui
297 elmex 1.47 if ($key eq 'mapsize' and $cfg->{$key} > 100) {
298     $cfg->{$key} = 100;
299 elmex 1.48 } elsif ($key eq 'mapsize' and $cfg->{$key} < 50) {
300     $cfg->{$key} = 50;
301 elmex 1.47 }
302 elmex 1.23 });
303     }
304    
305     sub run_config_dialog {
306     my (%events) = @_;
307    
308     my $w = Gtk2::Window->new;
309    
310     my @cfg = (
311     [qw/Host host/],
312     [qw/Port port/],
313 elmex 1.47 [qw/Mapsize% mapsize/],
314 elmex 1.23 [qw/Username user/],
315     [qw/Password password/],
316     );
317    
318     my $cfg = {};
319    
320     my $a = SDL::ListModes (0, SDL_FULLSCREEN|SDL_HWSURFACE);
321     my @modes = map { [SDL::RectW ($_), SDL::RectH ($_)] } @$a;
322    
323     $w->add (my $vb = Gtk2::VBox->new);
324     $vb->pack_start (my $t = Gtk2::Table->new (2, scalar @cfg), 0, 0, 0);
325     my $selmode = $::CFG->{width} . 'x' . $::CFG->{height};
326     $t->attach_defaults (Gtk2::Label->new ("Modes"), 0, 1, 0, 1);
327     $t->attach_defaults (my $cb = Gtk2::ComboBox->new_text, 1, 2, 0, 1);
328     my $i = 0;
329     my $act = 0;
330     for (map { "$_->[0]x$_->[1]" } reverse @modes) {
331     if ($_ eq $selmode) { $act = $i }
332     $cb->append_text ($_);
333     $i++;
334     }
335     $cb->set_active ($act);
336     $cb->signal_connect (changed => sub {
337     my ($cb) = @_;
338     my $txt = $cb->get_active_text;
339     if ($txt =~ m/(\d+)x(\d+)/) {
340     $::CFG->{width} = $1;
341     $::CFG->{height} = $2;
342     }
343     });
344    
345     $cfg->{_i} = 1;
346     for (@cfg) {
347     gtk_add_cfg_field ($t, $cfg, $_->[0], $_->[1], $::CFG->{$_->[1]});
348     }
349    
350     $vb->pack_start (my $hb = Gtk2::HBox->new, 0, 0, 0);
351     $hb->pack_start (my $cb = Gtk2::Button->new ("save"), 1, 1, 5);
352     $cb->signal_connect (clicked => sub {
353     for (keys %$cfg) {
354     $::CFG->{$_} = $cfg->{$_}
355     if $_ ne '_i';
356     }
357     Crossfire::Client::write_cfg "$Crossfire::VARDIR/pclientrc";
358     });
359     $hb->pack_start (my $cb = Gtk2::Button->new ("login"), 1, 1, 5);
360     $cb->signal_connect (clicked => sub {
361     for (keys %$cfg) {
362     $::CFG->{$_} = $cfg->{$_}
363     if $_ ne '_i';
364     }
365     my $cb = $events{login} || sub {};
366     $cb->($::CFG->{user}, $::CFG->{password});
367     });
368     $hb->pack_start (my $cb = Gtk2::Button->new ("logout"), 1, 1, 5);
369     $cb->signal_connect (clicked => sub {
370 root 1.50 my $cb = $events{logout} || sub {};
371 elmex 1.23 $cb->();
372     });
373     $hb->pack_start (my $cb = Gtk2::Button->new ("quit"), 1, 1, 5);
374     $cb->signal_connect (clicked => sub { $w->destroy });
375    
376     $w->show_all;
377    
378     $w->signal_connect (destroy => sub { Gtk2->main_quit });
379     }
380    
381    
382 root 1.1 #############################################################################
383    
384 root 1.24 SDL::Init SDL_INIT_EVERYTHING;
385 elmex 1.20
386 elmex 1.41 $TOPLEVEL = Crossfire::Client::Widget::Toplevel->new;
387    
388 elmex 1.20 my $mapwidget = Crossfire::Client::Widget::MapWidget->new;
389    
390 elmex 1.41 $TOPLEVEL->add ($mapwidget);
391 elmex 1.16 $mapwidget->focus_in;
392    
393 root 1.22 Crossfire::Client::read_cfg "$Crossfire::VARDIR/pclientrc";
394 elmex 1.10
395 root 1.13 $CFG ||= {
396     width => 640,
397     height => 480,
398 elmex 1.47 mapsize => 100,
399 root 1.13 fullscreen => 0,
400     host => "crossfire.schmorp.de",
401     port => 13327,
402     };
403 elmex 1.12
404 root 1.42 Crossfire::Client::set_font Crossfire::Client::find_rcfile "uifont.ttf";
405 root 1.40
406 root 1.24 $FACECACHE = eval { Crossfire::load_ref "$Crossfire::VARDIR/pclient.faces" } || {};
407    
408 elmex 1.23 run_config_dialog
409 elmex 1.16 login => sub { start_game },
410     logout => sub { stop_game };
411 root 1.1
412 root 1.13 main Gtk2;
413 root 1.19
414     Crossfire::save_ref $FACECACHE, "$Crossfire::VARDIR/pclient.faces";