ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/kgsueme/kgsueme/app.pl
Revision: 1.25
Committed: Mon Jun 7 13:37:08 2004 UTC (19 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.24: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 pcg 1.1 package app;
2    
3     use KGS::Protocol::Client;
4    
5     use Scalar::Util;
6    
7     use base KGS::Listener;
8 pcg 1.9
9     use Glib::Object::Subclass
10     Gtk2::Window;
11 pcg 1.1
12     my %context_id;
13    
14 pcg 1.9 our $self;
15    
16 pcg 1.1 sub status {
17     my ($type, $text) = @_;
18    
19 pcg 1.9 $::self->{status}->pop ($context_id{$type}) if $context_id{$type};
20     $::self->{status}->push ($context_id{$type} ||= $::self->{status}->get_context_id ($type), $text) if $text;
21 pcg 1.1 }
22    
23     sub new {
24 pcg 1.10 my ($self, %arg) = @_;
25 root 1.22
26 pcg 1.9 $self = $self->Glib::Object::new;
27 pcg 1.10 $self->{$_} = delete $arg{$_} for keys %arg;
28 root 1.22 gtk::state $self, "main::window", undef, window_size => [700, 580];
29    
30 root 1.23 $self->set (allow_shrink => 1);
31 pcg 1.1
32 pcg 1.9 Scalar::Util::weaken ($::self = $self); # singleton...
33 pcg 1.1
34 root 1.20 $self->{protocol} = new KGS::Protocol::Client;
35 pcg 1.1
36 root 1.20 $self->{roomlist} = new roomlist app => $self;
37 pcg 1.1
38 pcg 1.9 $self->set_title ('kgsueme');
39     $self->signal_connect (destroy => sub { %{$_[0]} = () });
40     $self->signal_connect (delete_event => sub { main_quit Gtk2; 1 });
41 pcg 1.1
42 pcg 1.9 $self->add (my $vbox = new Gtk2::VBox);
43 pcg 1.1
44 pcg 1.9 $vbox->pack_start (($buttonbox = new Gtk2::HButtonBox), 0, 1, 0);
45     $buttonbox->set_spacing (0);
46 pcg 1.1
47     my $button = sub {
48 pcg 1.9 $buttonbox->add (my $button = new Gtk2::Button $_[0]);
49 pcg 1.1 signal_connect $button clicked => $_[1];
50     };
51    
52     $button->("Login", sub { $self->login; });
53     $button->("Roomlist", sub { $self->{roomlist}->show; });
54 root 1.22 $button->("Save Layout", \&util::save_config);
55 root 1.21 $button->("Logout", sub { $self->{conn}->send ("quit") if $self->{conn} });
56 pcg 1.1 $button->("Quit", sub { main_quit Gtk2 });
57    
58 pcg 1.9 $vbox->pack_start ((my $hbox = new Gtk2::HBox), 0, 1, 0);
59 pcg 1.1
60 pcg 1.9 $hbox->add (new Gtk2::Label "Login");
61 pcg 1.1
62 pcg 1.9 $hbox->add ($self->{login} = new_with_max_length Gtk2::Entry 12);
63     $self->{login}->set_text ($::config->{login});
64 pcg 1.1
65 pcg 1.9 $hbox->add (new Gtk2::Label "Password");
66     $hbox->add ($self->{password} = new Gtk2::Entry);
67 root 1.16 $self->{password}->set (visibility => 0, is_focus => 1);
68     $self->{password}->signal_connect (activate => sub { $self->login });
69 pcg 1.1
70 pcg 1.13 $vbox->pack_start ((my $vpane = new Gtk2::VPaned), 1, 1, 0);
71     $vpane->set (position_set => 1);
72 root 1.22 gtk::state $vpane, "main::vpane", undef, position => 300;
73 pcg 1.13
74     $vbox->pack_start(($self->{status} = new Gtk2::Statusbar), 0, 1, 0);
75    
76 root 1.20 $self->{gamelist} = new gamelist app => $self;
77 pcg 1.13 $vpane->pack1 ($self->{gamelist}, 1, 1);
78    
79     $self->{rooms} = new Gtk2::Notebook;
80 root 1.18 $self->{rooms}->set (enable_popup => 1, scrollable => 1);
81 pcg 1.1
82 pcg 1.13 $vpane->pack2 ($self->{rooms}, 1, 1);
83 pcg 1.1
84 pcg 1.9 $self->show_all;
85 pcg 1.1
86     $self;
87     }
88    
89     sub login {
90     my ($self) = @_;
91    
92 root 1.20 $self->{protocol}->disconnect;
93 pcg 1.1
94     # initialize new socket and connection
95 pcg 1.12 my $sock = new IO::Socket::INET PeerHost => KGS::Protocol::KGSHOST, PeerPort => KGS::Protocol::KGSPORT
96 root 1.20 or do {
97     app::status ("connect", "connect: $!");
98     $self->event_quit;
99     return;
100     };
101    
102     $self->{login}->set (sensitive => 0);
103     $self->{password}->set (sensitive => 0);
104 pcg 1.1
105 root 1.15 $sock->blocking (1);
106 root 1.20 $self->{protocol}->handshake ($sock);
107 root 1.15 $sock->blocking (0);
108 pcg 1.1
109 root 1.20 $self->listen ($self->{protocol});
110     $self->{roomlist}->listen ($self->{protocol});
111     #$self->{gamelist}->listen ($conn);
112    
113     # now login
114 root 1.24 $ENV{KGSUEME_CLIENTVER} ||= "1.4.2_03:Swing app:Sun Microsystems Inc."; # he asked for it...#d#
115 root 1.20 $self->{protocol}->login ($ENV{KGSUEME_CLIENTVER} || "kgsueme $VERSION $^O", # allow users to overwrite
116     $self->{login}->get_text,
117 root 1.22 $self->{password}->get_text,
118     "en_US");
119 root 1.20
120 pcg 1.1 my $input; $input = add_watch Glib::IO fileno $sock, [G_IO_IN, G_IO_ERR, G_IO_HUP], sub {
121     # this is dorked
122     my $buf;
123     my $len = sysread $sock, $buf, 16384;
124     if ($len) {
125 root 1.20 $self->{protocol}->feed_data ($buf);
126 pcg 1.1 } elsif (defined $len || (!$!{EINTR} and !$!{EAGAIN})) {
127     remove Glib::Source $input;
128 root 1.20 $self->{protocol}->disconnect;
129     warn "disconnect: $!";#TODO#
130     app::status ("disconnect", "$!");#TODO# not visible
131 pcg 1.1 }
132     1;
133     }, G_PRIORITY_HIGH;
134     }
135    
136 pcg 1.5 sub inject_login {
137 pcg 1.1 my ($self, $msg) = @_;
138    
139 pcg 1.5 $self->{name} = $self->{conn}{name};
140 pcg 1.1
141 pcg 1.5 $::config->{login} = $self->{name};
142 pcg 1.1
143 pcg 1.6 app::status("login", "logged in as '$self->{name}' with status '$msg->{message}' ('$msg->{reason}')");
144 pcg 1.1
145     if ($msg->{success}) {
146     # auto-join
147     $self->open_room (%$_) for values %{$::config->{rooms}};
148    
149     sound::play 3, "connect";
150     } elsif ($msg->{result} eq "user unknown") {
151     sound::play 2, "user_unknown";
152     } else {
153     sound::play 2, "warning";
154     }
155     }
156    
157 root 1.20 sub listen {
158     my $self = shift;
159    
160 root 1.22 $self->SUPER::listen (@_, qw(login userpic idle_warn msg_chat chal_defaults upd_rooms));
161 root 1.20 }
162    
163 pcg 1.5 sub inject_idle_warn {
164     my ($self, $msg) = @_;
165    
166     $self->send ("idle_reset");
167     }
168    
169     sub inject_msg_chat {
170     my ($self, $msg) = @_;
171    
172     if ((lc $msg->{name2}) eq (lc $self->{name})) {
173     unless ($self->{user}{lc $msg->{name}}) {
174     $self->open_user (name => $msg->{name})->inject ($msg);
175 root 1.17 sound::play 2, "ring";
176 pcg 1.5 }
177 root 1.22 }
178     }
179    
180     sub inject_upd_rooms {
181     my ($self, $msg) = @_;
182    
183     return if %{$self->{room}};
184    
185     # join default room
186     for (@{$msg->{rooms}}) {
187 root 1.25 $self->open_room (%$_) if $_->is_default;#d#
188 pcg 1.5 }
189     }
190    
191 pcg 1.14 sub inject_chal_defaults {
192     my ($self, $msg) = @_;
193    
194     $self->{defaults} = $msg->{defaults};
195     }
196    
197 pcg 1.1 my %userpic;
198     my %userpic_cb;
199    
200     # static method to request the userimage and call the cb when it's available.
201     sub userpic {
202     my ($self, $name, $cb) = @_;
203     $self->get_userpic ($name, $cb);
204     }
205    
206     sub get_userpic {
207     my ($self, $name, $cb) = @_;
208    
209     if (exists $userpic{$name}) {
210     $cb->($userpic{$name});
211     } else {
212     if (!exists $userpic_cb{$name}) {
213     # after 10 seconds, flush callbacks
214     $self->send (req_pic => name => $name);
215     add Glib::Timeout 10000, sub {
216     $_->() for @{delete $userpic_cb{$name} || []};
217     0;
218     };
219     }
220     push @{$userpic_cb{$name}}, $cb;
221     }
222     }
223    
224     sub inject_userpic {
225     my ($self, $msg) = @_;
226    
227     $userpic{$msg->{name}} = $msg->{data};
228     $_->($userpic{$msg->{name}}) for @{delete $userpic_cb{$msg->{name}} || []};
229     }
230    
231 root 1.20 sub event_quit {
232     my ($self) = @_;
233    
234     $self->SUPER::event_quit;
235    
236     $self->{login}->set (sensitive => 1);
237     $self->{password}->set (sensitive => 1);
238    
239     sound::play 2, "warning";
240 root 1.15 }
241 pcg 1.1
242     sub open_game {
243     my ($self, %arg) = @_;
244    
245     ($self->{game}{$arg{channel}} ||= new game %arg, conn => $self->{conn}, app => $self)
246     ->join;
247 pcg 1.4 Scalar::Util::weaken $self->{game}{$arg{channel}};
248 pcg 1.2 $self->{game}{$arg{channel}};
249 pcg 1.1 }
250    
251     sub open_room {
252     my ($self, %arg) = @_;
253    
254 pcg 1.13 my $room = $self->{room}{$arg{channel}} ||= do {
255     my $room = new room %arg, conn => $self->{conn}, app => $self;
256     $room->show_all;
257     $self->{rooms}->append_page ($room, new Gtk2::Label $room->{name});
258     $room;
259     };
260 pcg 1.8 Scalar::Util::weaken $self->{room}{$arg{channel}};
261 pcg 1.13
262     $room->join;
263 pcg 1.2 $self->{room}{$arg{channel}};
264     }
265    
266     sub open_user {
267     my ($self, %arg) = @_;
268    
269 pcg 1.3 ($self->{user}{lc $arg{name}} ||= new user %arg, conn => $self->{conn}, app => $self)
270 pcg 1.2 ->join;
271 pcg 1.4 Scalar::Util::weaken $self->{user}{lc $arg{name}};
272 pcg 1.3 $self->{user}{lc $arg{name}};
273 pcg 1.2 }
274    
275     sub do_command {
276     my ($self, $chat, $cmd, $arg, %arg) = @_;
277    
278 pcg 1.5 if ($cmd eq "say") {
279     if (my $context = $arg{game} || $arg{room} || $arg{user}) {
280 pcg 1.2 $context->say ($arg);
281     } else {
282     $chat->append_text ("\n<error>no context for message</error>");
283     }
284     } elsif ($cmd eq "whois" or $cmd eq "w") {
285     $self->open_user (name => $arg);
286     } else {
287     $chat->append_text ("\n<error>unknown command</error>");
288     }
289 pcg 1.1 }
290    
291     1;
292