ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/kgsueme/bin/kgsueme
Revision: 1.12
Committed: Fri May 30 03:25:48 2003 UTC (20 years, 11 months ago) by pcg
Branch: MAIN
Changes since 1.11: +250 -140 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 elmex 1.6 #!/usr/bin/perl -I../lib/
2 pcg 1.1
3 elmex 1.6 #use PApp::Util qw(dumpval); # debug only
4 pcg 1.1
5     use Gtk;
6     use Gtk::Gdk;
7 pcg 1.12 use Gtk::Gdk::Pixbuf;
8     #use Gtk::Gdk::ImlibImage;
9 pcg 1.1
10     use KGS::Protocol;
11     use KGS::Listener::Debug;
12    
13     use IO::Socket::INET;
14    
15     use Errno;
16    
17 pcg 1.12 init Gtk;
18 pcg 1.1
19 pcg 1.3 $HACK = 1; # do NEVER enable. ;)
20    
21 pcg 1.2 our $config;
22 pcg 1.7 our $IMGDIR = "images";
23    
24     sub load_img {
25 pcg 1.12 new_from_file Gtk::Gdk::Pixbuf "$IMGDIR/$_[0]"
26     # load_image Gtk::Gdk::ImlibImage "$IMGDIR/$_[0]"
27 pcg 1.7 or die "$IMGDIR/$_[0]: $!";
28     }
29    
30 pcg 1.12 our @black_img = load_img "b-01.png";
31     our @white_img = map +(load_img "w-0$_.png"), 1,2,3,4,5;
32     our @triangle_img = map +(load_img "triangle-$_.png"), qw(b w);
33     our @square_img = map +(load_img "square-$_.png"), qw(b w);
34     our @circle_img = map +(load_img "circle-$_.png"), qw(b w);
35     our $board_img = load_img "woodgrain-01.jpg";
36 pcg 1.2
37 pcg 1.1 {
38     use Storable ();
39     use Scalar::Util ();
40    
41     my $staterc = "$ENV{HOME}/.kgsueme";
42    
43     my $state = -r $staterc ? Storable::retrieve($staterc) : {};
44     my @widgets;
45    
46 pcg 1.2 $config = $state->{config} ||= {};
47    
48 pcg 1.1 # grr... more gtk+ brokenness
49     my %get = (
50 pcg 1.8 hpane_position => sub { ($_[0]->children)[0]->allocation->[2] },
51     vpane_position => sub { ($_[0]->children)[0]->allocation->[3] },
52 pcg 1.1 window_size => sub { [ @{$_[0]->allocation}[2,3] ] },
53     #window_pos => sub { die PApp::Util::dumpval [ $_[0]->get_root_origin ] },
54     clist_column_widths => sub {
55     $_[0]{column_widths};
56     },
57     );
58    
59     my %set = (
60     hpane_position => sub { $_[0]->set_position($_[1]) },
61     vpane_position => sub { $_[0]->set_position($_[1]) },
62     window_size => sub { $_[0]->set_default_size(@{$_[1]}) },
63     #window_pos => sub { $_[0]->set_uposition(@{$_[1]}) if @{$_[1]} },
64     clist_column_widths => sub {
65     my ($w, $v) = @_;
66     $v->[$_] && $w->set_column_width($_, $v->[$_]) for 0..$#$v;
67     $w->{column_widths} = $v;
68     $w->signal_connect(resize_column => sub { $v->[$_[1]] = $_[2]; });
69     },
70     );
71    
72     sub state {
73     my ($widget, $class, $instance, %attr) = @_;
74    
75     while (my ($k, $v) = each %attr) {
76     my ($set, $get) = $k =~ /=/ ? split /=/, $k : ($k, $k);
77     $v = $state->{$class}{"*"}{$get} if exists $state->{$class}{"*"}{$get};
78     $v = $state->{$class}{$instance}{$get} if exists $state->{$class}{$instance}{$get};
79     $set{$get} ? $set{$get}->($widget, $v) : $widget->set($set => $v);
80     }
81    
82     $widget = [$widget, $class, $instance, \%attr];
83     Scalar::Util::weaken $widget->[0];
84    
85     @widgets = (grep $_->[0], @widgets, $widget);
86     }
87    
88     sub save_state {
89     for (@widgets) {
90     if ($_->[0]) {
91     my ($widget, $class, $instance, $attr) = @$_;
92 pcg 1.7
93     $widget->realize;
94    
95 pcg 1.1 while (my ($k, $v) = each %$attr) {
96     my ($set, $get) = $k =~ /=/ ? split /=/, $k : ($k, $k);
97     $v = $get{$get} ? $get{$get}->($widget) : $widget->get($get);
98    
99     $state->{$class}{"*"}{$get} = $v;
100     $state->{$class}{$instance}{$get} = $v;
101     }
102     }
103     ::status("save_state", "layout saved");
104     }
105    
106     Storable::nstore($state, $staterc);
107     }
108     }
109    
110     # make a clist unselectable
111     sub clist_autosort {
112     my $w = shift;
113     my ($c, $o) = (-1);
114     for (0..$w->columns-1) {
115     $w->signal_connect(click_column => sub {
116     if ($_[1] != $c) {
117     $c = $_[1];
118     $o = 0;
119     } else {
120     $o = !$o;
121     }
122     $w->set_sort_column($c);
123     $w->set_sort_type($o ? "descending" : "ascending");
124     $w->sort;
125     });
126     }
127    
128     }
129    
130     {
131     my $main = new kgsueme;
132    
133     my %context_id;
134    
135     sub status {
136     my ($type, $text) = @_;
137    
138     $main->{status}->pop($context_id{$type}) if $context_id{$type};
139     $main->{status}->push($context_id{$type} ||= $main->{status}->get_context_id($type), $text) if $text;
140     }
141     }
142    
143 pcg 1.12 if (0) {
144     my $board = new game size => 5;
145     $board->{window}->show_all;
146     }
147    
148 pcg 1.1 main Gtk;
149    
150     #############################################################################
151    
152     package kgsueme;
153    
154     use base KGS::Listener;
155    
156     sub new {
157     my $self = shift;
158     $self = $self->SUPER::new(@_);
159    
160     $self->{conn} = new KGS::Protocol;
161    
162     KGS::Listener::Debug->new->listen($self->{conn}); #d# debug only :)
163    
164     $self->listen($self->{conn});
165    
166 pcg 1.4 $self->{roomlist} = new roomlist conn => $self->{conn};
167    
168 pcg 1.1 $self->{window} = new Gtk::Window 'toplevel';
169     $self->{window}->set_title('kgsueme');
170     ::state $self->{window}, "main::window", undef, window_size => [400, 100];
171 pcg 1.7 $self->{window}->signal_connect(delete_event => sub { main_quit Gtk; 1 });
172 pcg 1.1
173     $self->{window}->add(my $vbox = new Gtk::VBox);
174    
175 pcg 1.4 $vbox->pack_start(($buttonbox = new Gtk::HButtonBox), 0, 1, 0);
176     $buttonbox->set_spacing(0);
177 pcg 1.1
178 pcg 1.2 my $button = sub {
179 pcg 1.4 $buttonbox->add(my $button = new Gtk::Button $_[0]);
180 pcg 1.2 signal_connect $button clicked => $_[1];
181     };
182 pcg 1.1
183 pcg 1.4 $button->("Login", sub { $self->login; });
184     $button->("Roomlist", sub { $self->{roomlist}->show; });
185 pcg 1.2 $button->("Save Config & Layout", sub { ::save_state });
186 pcg 1.4 $button->("Quit", sub { main_quit Gtk });
187 pcg 1.1
188     $vbox->pack_start((my $hbox = new Gtk::HBox), 0, 1, 0);
189    
190     $hbox->add(new Gtk::Label "Login");
191    
192     $hbox->add($self->{login} = new_with_max_length Gtk::Entry 12);
193 pcg 1.2 $self->{login}->set_text($::config->{login});
194 pcg 1.3
195     if ($::HACK) {
196     $self->{login}->signal_connect(activate => sub {
197     $self->{conn}{name} = $self->{login}->get_text;
198     });
199     }
200 pcg 1.1
201     $hbox->add(new Gtk::Label "Password");
202 pcg 1.2 $hbox->add($self->{password} = new Gtk::Entry);
203     $self->{password}->set_visibility(0);
204 pcg 1.1
205     $vbox->pack_start(($self->{status} = new Gtk::Statusbar), 0, 1, 0);
206    
207     $self->{window}->show_all;
208    
209     $self;
210     }
211    
212     sub login {
213     my ($self) = @_;
214    
215 pcg 1.2 $self->{conn}->disconnect;
216    
217 pcg 1.1 # initialize new socket and connection
218     my $sock = new IO::Socket::INET PeerHost => "kgs.kiseido.com", PeerPort => "2379"
219     or die;
220    
221     $sock->blocking(1);
222     $self->{conn}->handshake($sock);
223     $sock->blocking(0);
224    
225     my $input; $input = input_add Gtk::Gdk fileno $sock, "read", sub {
226     # this is dorked
227     my $buf;
228     if (0 >= sysread $sock, $buf, 16384
229     and !$!{EINTR} and !$!{EAGAIN}) {
230     input_remove Gtk::Gdk $input;
231     $self->event_disconnect;
232     }
233     $self->{conn}->feed_data($buf);
234     };
235    
236     # now login
237 pcg 1.2 $self->{conn}->login($self->{login}->get_text, $self->{password}->get_text);
238 pcg 1.1 }
239    
240     sub inject_login {
241     my ($self, $msg) = @_;
242    
243     ::status("login", "logged in as '$self->{conn}{name}' with status '$msg->{result}'");
244 pcg 1.2 $::config->{login} = $self->{conn}{name};
245 pcg 1.1
246 pcg 1.4 if ($msg->{success}) {
247     for (keys %{$::config->{rooms}}) {
248     $self->{roomlist}->join_room($_);
249     }
250     }
251    
252     warn PApp::Util::dumpval($::config);
253 pcg 1.1 }
254    
255     sub event_disconnect { }
256    
257     #############################################################################
258    
259     package roomlist;
260    
261     use base KGS::Listener::Roomlist;
262    
263     sub new {
264     my $self = shift;
265     $self = $self->SUPER::new(@_);
266    
267     $self->listen($self->{conn});
268    
269     $self->{window} = new Gtk::Window 'toplevel';
270     $self->{window}->set_title('KGS Rooms');
271     ::state $self->{window}, "roomlist::window", undef, window_size => [400, 200];
272    
273 pcg 1.7 $self->{window}->signal_connect(delete_event => sub { $self->{window}->hide; 1 });
274 pcg 1.1
275     $self->{window}->add(my $vbox = new Gtk::VBox);
276    
277     $vbox->pack_start((my $sw = new Gtk::ScrolledWindow), 1, 1, 0);
278     $sw->set_policy("automatic", "always");
279    
280     $sw->add($self->{roomlist} = new_with_titles Gtk::CList "Group", "Room Name", "Users", "Games", "Flags", "Channel");
281     $self->{roomlist}->set_selection_mode('multiple');
282     ::clist_autosort $self->{roomlist};
283     ::state $self->{roomlist}, "roomlist::roomlist", undef, clist_column_widths => [20, 200];
284    
285     $self->{roomlist}->signal_connect(select_row => sub {
286     my $room = $self->{roomlist}->get_row_data($_[1])
287     or return;
288     $self->{roomlist}->unselect_all;
289 pcg 1.4 $self->join_room($room->{channel});
290 pcg 1.1 });
291    
292     $self;
293     }
294    
295 pcg 1.4 sub join_room {
296     my ($self, $channel) = @_;
297    
298     $self->{room}{$channel} ||= room->new(channel => $channel, conn => $self->{conn}, users => {});
299     $self->{room}{$channel}->join;
300     }
301    
302 pcg 1.1 sub show {
303     my ($self, $msg) = @_;
304    
305     $self->msg(list_rooms => group => $_) for 0..5; # fetch all room names (should not!)
306     $self->{window}->show_all;
307     }
308    
309 pcg 1.9 sub event_update_rooms {
310 pcg 1.1 my ($self) = @_;
311    
312     $self->{event_update} ||= Gtk->timeout_add(200, sub {
313     my $l = $self->{roomlist};
314    
315     $l->freeze;
316     my $pos = $l->get_vadjustment->get_value;
317     $l->clear;
318    
319     my $row = 0;
320     for (values %{$self->{rooms}}) {
321     $l->append($_->{group}, $_->{name}, $_->{users}, $_->{games}, $_->{flags}, $_->{channel});
322     $l->set_row_data($row++, $_);
323     }
324     $l->sort;
325     $l->get_vadjustment->set_value($pos);
326     $l->thaw;
327    
328     delete $self->{event_update};
329     0;
330     });
331     }
332    
333     #############################################################################
334    
335     package room;
336    
337     use base KGS::Listener::Room;
338    
339     sub new {
340     my $self = shift;
341     $self = $self->SUPER::new(@_);
342    
343     $self->listen($self->{conn});
344    
345     $self->{window} = new Gtk::Window 'toplevel';
346     $self->{window}->set_title("KGS Room $self->{name}");
347     ::state $self->{window}, "room::window", $self->{name}, window_size => [600, 400];
348    
349 pcg 1.7 $self->{window}->signal_connect(delete_event => sub { $self->part; 1 });
350 pcg 1.1
351     $self->{window}->add(my $hpane = new Gtk::HPaned);
352     ::state $hpane, "room::hpane", $self->{name}, hpane_position => 200;
353    
354     $hpane->add(my $vpane = new Gtk::VPaned);
355     ::state $vpane, "room::vpane", $self->{name}, vpane_position => 200;
356    
357     $vpane->add(my $sw = new Gtk::ScrolledWindow);
358     $sw->set_policy("automatic", "always");
359    
360     $sw->add($self->{gamelist} = new_with_titles Gtk::CList "T", "Black", "White", "Rules", "Notes");
361     ::clist_autosort $self->{gamelist};
362     ::state $self->{gamelist}, "room::gamelist", $self->{name}, clist_column_widths => [20, 120, 120, 120];
363    
364     $self->{gamelist}->signal_connect(select_row => sub {
365     my $game = $self->{gamelist}->get_row_data($_[1])
366     or return;
367     $self->{game}{$game->{channel}} ||= new game %$game, conn => $self->{conn};
368     $self->{game}{$game->{channel}}->join;
369     $self->{gamelist}->unselect_all;
370     });
371    
372     $vpane->add(my $vbox = new Gtk::VBox);
373    
374     $vbox->pack_start((my $sw = new Gtk::ScrolledWindow), 1, 1, 0);
375     $sw->set_policy("automatic", "always");
376    
377     $sw->add($self->{text} = new Gtk::Text);
378    
379     $vbox->pack_start(($self->{entry} = new Gtk::Entry), 0, 1, 0);
380     $self->{entry}->signal_connect(activate => sub {
381     my $text = $self->{entry}->get_text;
382     $self->say($text) if $text =~ /\S/;
383     $self->{entry}->set_text("");
384     });
385    
386     $hpane->add(my $sw = new Gtk::ScrolledWindow);
387     $sw->set_policy("automatic", "always");
388    
389     $sw->add($self->{userlist} = new_with_titles Gtk::CList "User", "Rank", "Flags");
390     ::clist_autosort $self->{userlist};
391     ::state $self->{userlist}, "room::userlist", $self->{name}, clist_column_widths => [120, 30];
392    
393     $self;
394     }
395    
396 pcg 1.9 sub event_update_users {
397 pcg 1.1 my ($self) = @_;
398    
399     $self->{event_update} ||= Gtk->timeout_add(200, sub {
400     my $l = $self->{userlist};
401    
402     $l->freeze;
403     my $pos = $l->get_vadjustment->get_value;
404     $l->clear;
405    
406     my $row = 0;
407     for (values %{$self->{users}}) {
408     $l->append($_->{name});
409     $l->set_row_data($row++, $_);
410     }
411     $l->sort;
412     $l->get_vadjustment->set_value($pos);
413     $l->thaw;
414    
415     delete $self->{event_update};
416     0;
417     });
418     }
419    
420     sub event_update_games {
421     my ($self) = @_;
422    
423     $self->{event_update_games} ||= Gtk->timeout_add(200, sub {
424     my $l = $self->{gamelist};
425    
426     $l->freeze;
427     my $pos = $l->get_vadjustment->get_value;
428     $l->clear;
429    
430     my $row = 0;
431     for (values %{$self->{games}}) {
432     $l->append($_->type, $_->user0, $_->user1, $_->rules, $_->notes);
433     $l->set_row_data($row++, $_);
434     }
435     $l->sort;
436     $l->get_vadjustment->set_value($pos);
437     $l->thaw;
438    
439     delete $self->{event_update_games};
440     0;
441     });
442     }
443    
444     sub join {
445     my ($self) = @_;
446     $self->SUPER::join;
447    
448     $self->{window}->show_all;
449     }
450    
451     sub part {
452     my ($self) = @_;
453     $self->SUPER::part;
454    
455 pcg 1.4 delete $::config->{rooms}{$self->{channel}};
456 pcg 1.1 $self->{window}->hide_all;
457 pcg 1.12 $self->event_update_users;
458 pcg 1.1 $self->event_update_games;
459 pcg 1.4 }
460    
461     sub event_join {
462     my ($self) = @_;
463     $self->SUPER::event_join;
464    
465     $::config->{rooms}{$self->{channel}} = 1;
466 pcg 1.1 }
467    
468     sub event_update_roominfo {
469     my ($self) = @_;
470    
471 pcg 1.7 $self->{text}->insert(undef, undef, undef, "\n$self->{owner}: $self->{description}\n");
472 pcg 1.1 }
473    
474     sub inject_msg_room {
475     my ($self, $msg) = @_;
476     return unless $self->{channel} == $msg->{channel};
477    
478 pcg 1.7 $self->{text}->insert(undef, undef, undef, "\n$msg->{name}: $msg->{message}");
479 pcg 1.1 }
480    
481     #############################################################################
482    
483     package game;
484    
485 pcg 1.7 use KGS::Constants;
486 pcg 1.9 use KGS::Game::Board;
487 pcg 1.7
488 pcg 1.1 use base KGS::Listener::Game;
489     use base KGS::Game;
490    
491     sub new {
492     my $self = shift;
493     $self = $self->SUPER::new(@_);
494    
495     $self->listen($self->{conn});
496    
497     $self->{window} = new Gtk::Window 'toplevel';
498 pcg 1.12 $self->{window}->set_title("KGS Game ".$self->user0." ".$self->user1) if $self->{channel};#d#
499 pcg 1.1 ::state $self->{window}, "game::window", undef, window_size => [600, 500];
500    
501 pcg 1.7 $self->{window}->signal_connect(delete_event => sub { $self->part; 1 });
502 pcg 1.1
503     $self->{window}->add(my $hpane = new Gtk::HPaned);
504     ::state $hpane, "game::hpane", undef, hpane_position => 500;
505    
506 pcg 1.12 Gtk::Widget->push_visual (Gtk::Gdk::Rgb->get_visual);
507     Gtk::Widget->push_colormap (Gtk::Gdk::Rgb->get_cmap);
508     $hpane->pack1(($self->{canvas} = new Gtk::DrawingArea), 1, 1);
509     Gtk::Widget->pop_colormap;
510     Gtk::Widget->pop_visual;
511 pcg 1.7
512 pcg 1.12 $self->{canvas}->signal_connect(configure_event => \&configure_event, $self);
513     $self->{canvas}->signal_connect(expose_event => \&expose_event, $self);
514 pcg 1.1
515 pcg 1.11 $hpane->pack2((my $vpane = new Gtk::VPaned), 0, 0);
516 pcg 1.1 ::state $vpane, "game", $self->{name}, vpane_position => 80;
517    
518     $vpane->add(my $sw = new Gtk::ScrolledWindow);
519     $sw->set_policy("automatic", "always");
520    
521     $sw->add($self->{userlist} = new_with_titles Gtk::CList "User", "Rank", "Flags");
522     ::clist_autosort $self->{userlist};
523     ::state $self->{userlist}, "room::userlist", $self->{name}, clist_column_widths => [120, 30];
524    
525     $vpane->add(my $vbox = new Gtk::VBox);
526    
527     $vbox->pack_start((my $sw = new Gtk::ScrolledWindow), 1, 1, 0);
528     $sw->set_policy("automatic", "always");
529    
530     $sw->add($self->{text} = new Gtk::Text);
531    
532     $vbox->pack_start(($self->{entry} = new Gtk::Entry), 0, 1, 0);
533     $self->{entry}->signal_connect(activate => sub {
534     my $text = $self->{entry}->get_text;
535     # add message
536     $self->{entry}->set_text("");
537     });
538    
539     $self;
540     }
541    
542 pcg 1.9 sub event_update_users {
543 pcg 1.1 my ($self) = @_;
544    
545 pcg 1.9 room::event_update_users $self;
546 pcg 1.1 }
547    
548     sub join {
549     my ($self) = @_;
550     $self->SUPER::join;
551    
552     $self->{window}->show_all;
553     }
554    
555     sub part {
556     my ($self) = @_;
557     $self->SUPER::part;
558    
559 pcg 1.10 $self->{window}->hide;
560 pcg 1.1 }
561    
562 pcg 1.12 sub configure_event {
563     my ($widget, $self, $event) = @_;
564     $self->repaint_board;
565     1;
566     }
567    
568     sub expose_event {
569     my ($widget, $self, $event) = @_;
570    
571     $self->{pixmap} or return;
572    
573     my ($ox, $oy, $s) = @{$self->{offsets}};
574    
575     my ($x, $y, $w, $h) =
576     @{Gtk::Gdk::Rectangle->intersect(
577     $event->{area},
578     [$ox, $oy, $s, $s]
579     )};
580    
581     warn $ox;
582     $self->{canvas}->window->draw_pixmap (
583     $self->{canvas}->style->white_gc,
584     $self->{pixmap},
585     $x - $ox, $y - $oy, $x, $y, $w, $h,
586     );
587     1;
588     }
589    
590     # create new, _transparent_ pixbuf
591     sub new_pixbuf {
592     my ($w, $h) = @_;
593    
594     $pixbuf;
595     }
596    
597     sub INTERP_TILES (){ 1 }
598     sub INTERP_BILINEAR (){ 2 }
599     sub INTERP_HYPER (){ 3 }
600    
601     sub new_pixbuf {
602     my ($w, $h, $clear) = @_;
603    
604     my $pixbuf = new Gtk::Gdk::Pixbuf 'rgb', 1, 8, $w, $h;
605    
606     if ($clear) { # damn, need to clear it ourselves
607     my $row = "\x00\x00\x00\x00" x $w;
608     $pixbuf->put_pixels ($row, $_, 0) for 0 .. $h - 1;
609     }
610    
611     $pixbuf;
612     }
613    
614     sub scale_pixbuf {
615     my ($src, $w, $h, $mode) = @_;
616    
617     my $dst = new_pixbuf $w, $h;
618    
619     $src->scale(
620     $dst, 0, 0, $w, $h, 0, 0,
621     $w / $src->get_width, $h / $src->get_height,
622     $mode,
623     );
624    
625     $dst;
626     }
627    
628     sub label_font {
629     my ($size) = @_;
630    
631     $size = int $size;
632     $size = 34 if $size > 34;
633    
634     # I am soo incapable
635     for (0, 8, 10, 11, 12, 14, 17, 18, 20, 24, 25, 34) {
636     next unless $size <= $_;
637     my $font = Gtk::Gdk::Font->fontset_load ($size ? "-*-helvetica-bold-r-*--$_-*" : "fixed");
638     return $font if $font;
639     }
640    
641     die;
642     }
643    
644     sub center_text {
645     my ($drawable, $font, $gc, $x, $y, $t) = @_;
646     my $w = $font->string_width ($t);
647     my $h = $font->string_height($t) - $font->descent;
648     $drawable->draw_text ($font, $gc, $x - $w*0.5, $y + $h * 0.5, $t, length $t);
649     }
650    
651     my %cache;
652    
653     # create a stack of stones
654     sub create_stack {
655     my ($gc, $mark, $size) = @_;
656    
657     my $hash = "$size,$mark";
658     my $c = \$cache{$hash};
659     unless ($$c) {
660     for my $stone ($mark & (MARK_W | MARK_GRAY_W) ? @::white_img : @::black_img) {
661     my $base =
662     $mark & (MARK_B | MARK_GRAY_B | MARK_W | MARK_GRAY_W)
663     ? scale_pixbuf $stone, $size, $size, INTERP_HYPER
664     : new_pixbuf $size, $size, 1;
665    
666     if ($mark & (MARK_GRAY_B | MARK_GRAY_W)) {
667     # make transparent by stippling :(
668     # #d#
669     }
670    
671     for ([MARK_SMALL_B, $::black_img[int rand @::black_img]],
672     [MARK_SMALL_W, $::white_img[int rand @::white_img]]) {
673     my ($mask, $img) = @$_;
674     if ($mark & $mask) {
675     $img->composite (
676     $base, ($size / 3) x 6,
677     $size / $img->get_width / 3, $size / $img->get_height / 3,
678     INTERP_HYPER, 255
679     );
680     }
681     }
682    
683     my $dark_bg = ! ! ($mark & (MARK_B | MARK_GRAY_B));
684    
685     for ([MARK_CIRCLE, $::circle_img[$dark_bg]],
686     [MARK_TRIANGLE, $::triangle_img[$dark_bg]],
687     [MARK_SQUARE, $::square_img[$dark_bg]]) {
688     my ($mask, $img) = @$_;
689     if ($mark & $mask) {
690     $img->composite (
691     $base, 0, 0, $size, $size, 0, 0,
692     $size / $img->get_width, $size / $img->get_height,
693     INTERP_HYPER, 255
694     );
695     }
696     }
697    
698     push @$$c, [$base->render_pixmap_and_mask (128)];
699     }
700     }
701    
702     @{$$c->[int rand @$$c]};
703     }
704    
705     sub repaint_board {
706 pcg 1.1 my ($self) = @_;
707 pcg 1.12 my $canvas = $self->{canvas};
708    
709     %cache = ();
710    
711     my ($w, $h) = @{$canvas->allocation}[2,3];
712    
713     my $s = $w > $h ? $h : $w;
714    
715     $self->{offsets} = [int (($w - $s) / 2), int (($h - $s) / 2), $s];
716    
717     my $pixmap = $self->{pixmap} = new Gtk::Gdk::Pixmap $self->{canvas}->window, $s, $s;
718 pcg 1.1
719 pcg 1.12 {
720     my ($bw, $bh) = ($::board_img->get_width, $::board_img->get_height);
721    
722     my $bg = $s < $bw && $s < $bh ? $::board_img : scale_pixbuf $::board_img, $s, $s, INTERP_TILES;
723     $bg->render_to_drawable(
724     $pixmap, $self->{canvas}->style->white_gc,
725     0, 0, 0, 0, $s, $s,
726     0, 0, 0
727     );
728     }
729    
730     my $gc = Gtk::Gdk::GC->new ($pixmap);
731    
732     $gc->rgb_gc_set_foreground($line_colour);
733     $gc->set_line_attributes (int ($s / 300) + 1, 'solid', 'projecting', 'miter');
734    
735     my $size = $self->{size};
736     my $border = int ($s / $size * 0.75);
737     my $s2 = $s - $border * 2;
738     my $edge = int ($s2 / $size);
739     my $ofs = int ($s2 / $size * 0.5);
740    
741     my $font = label_font $ofs;
742    
743     my @k = map int ($s2 * $_ / $size - $ofs + $border + 0.5), 0 .. $size;
744    
745     my $a = "A";
746     for my $i (1 .. $size) {
747     $pixmap->draw_line ($gc, $k[$i], $k[1], $k[$i], $k[$size]);
748     $pixmap->draw_line ($gc, $k[1], $k[$i], $k[$size], $k[$i]);
749    
750     center_text $pixmap, $font, $gc, $k[$i], ($ofs +$border) / 2, $a;
751     center_text $pixmap, $font, $gc, $k[$i], $s2 + $border + $ofs / 2, $a;
752     center_text $pixmap, $font, $gc, ($ofs + $border) / 2, $k[$i], $i;
753     center_text $pixmap, $font, $gc, $s2 + $border + $ofs / 2, $k[$i], $i;
754    
755     $a++;
756     $a++ if $a eq "I"; # not correct, instead of AA AB, we should get HH JJ KK...
757     }
758    
759     # hoshi-points(!)#d#
760     # caching of empty board gfx(!)#d#
761 pcg 1.7
762 pcg 1.12 if ($self->{board}) {
763     for my $x (1 .. $size) {
764     for my $y (1 .. $size) {
765     my $yk = $s2 * $x / $size - $ofs + $border;
766     my $mark = $self->{board}{board}[$x-1][$y-1];
767    
768     if ($mark) {
769     my ($dx, $dy) = ($k[$x] - $ofs, $k[$y] - $ofs);
770     my ($pm, $bm) = create_stack $gc, $mark, $edge;
771    
772     $gc->set_clip_mask ($bm);
773     $gc->set_clip_origin ($dx, $dy);
774     $pixmap->draw_pixmap ($gc, $pm, 0, 0, $dx, $dy, $edge, $edge);
775     }
776     }
777 pcg 1.1 }
778     }
779 pcg 1.12 }
780    
781     sub event_update_tree {
782     my ($self) = @_;
783    
784     $self->{board} = new KGS::Game::Board $self->{size};
785     $self->{board}->interpret_path ($self->get_path);
786    
787     $self->repaint_board;
788    
789     # force a redraw (not perfect(?))
790     expose_event $self->{canvas}, $self, { area => $self->{canvas}->allocation };
791 pcg 1.9
792     $self->{text}->backward_delete($self->{text}->get_length);
793 pcg 1.12 $self->{text}->insert(undef, undef, undef, $self->{board}{comment}.PApp::Util::dumpval([$self->{board}{time},$self->{board}{captures}]));
794 pcg 1.1 }
795    
796     1;
797    
798    
799