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

# Content
1 package app;
2
3 use KGS::Protocol::Client;
4
5 use Scalar::Util;
6
7 use base KGS::Listener;
8
9 use Glib::Object::Subclass
10 Gtk2::Window;
11
12 my %context_id;
13
14 our $self;
15
16 sub status {
17 my ($type, $text) = @_;
18
19 $::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 }
22
23 sub new {
24 my ($self, %arg) = @_;
25
26 $self = $self->Glib::Object::new;
27 $self->{$_} = delete $arg{$_} for keys %arg;
28 gtk::state $self, "main::window", undef, window_size => [700, 580];
29
30 $self->set (allow_shrink => 1);
31
32 Scalar::Util::weaken ($::self = $self); # singleton...
33
34 $self->{protocol} = new KGS::Protocol::Client;
35
36 $self->{roomlist} = new roomlist app => $self;
37
38 $self->set_title ('kgsueme');
39 $self->signal_connect (destroy => sub { %{$_[0]} = () });
40 $self->signal_connect (delete_event => sub { main_quit Gtk2; 1 });
41
42 $self->add (my $vbox = new Gtk2::VBox);
43
44 $vbox->pack_start (($buttonbox = new Gtk2::HButtonBox), 0, 1, 0);
45 $buttonbox->set_spacing (0);
46
47 my $button = sub {
48 $buttonbox->add (my $button = new Gtk2::Button $_[0]);
49 signal_connect $button clicked => $_[1];
50 };
51
52 $button->("Login", sub { $self->login; });
53 $button->("Roomlist", sub { $self->{roomlist}->show; });
54 $button->("Save Layout", \&util::save_config);
55 $button->("Logout", sub { $self->{conn}->send ("quit") if $self->{conn} });
56 $button->("Quit", sub { main_quit Gtk2 });
57
58 $vbox->pack_start ((my $hbox = new Gtk2::HBox), 0, 1, 0);
59
60 $hbox->add (new Gtk2::Label "Login");
61
62 $hbox->add ($self->{login} = new_with_max_length Gtk2::Entry 12);
63 $self->{login}->set_text ($::config->{login});
64
65 $hbox->add (new Gtk2::Label "Password");
66 $hbox->add ($self->{password} = new Gtk2::Entry);
67 $self->{password}->set (visibility => 0, is_focus => 1);
68 $self->{password}->signal_connect (activate => sub { $self->login });
69
70 $vbox->pack_start ((my $vpane = new Gtk2::VPaned), 1, 1, 0);
71 $vpane->set (position_set => 1);
72 gtk::state $vpane, "main::vpane", undef, position => 300;
73
74 $vbox->pack_start(($self->{status} = new Gtk2::Statusbar), 0, 1, 0);
75
76 $self->{gamelist} = new gamelist app => $self;
77 $vpane->pack1 ($self->{gamelist}, 1, 1);
78
79 $self->{rooms} = new Gtk2::Notebook;
80 $self->{rooms}->set (enable_popup => 1, scrollable => 1);
81
82 $vpane->pack2 ($self->{rooms}, 1, 1);
83
84 $self->show_all;
85
86 $self;
87 }
88
89 sub login {
90 my ($self) = @_;
91
92 $self->{protocol}->disconnect;
93
94 # initialize new socket and connection
95 my $sock = new IO::Socket::INET PeerHost => KGS::Protocol::KGSHOST, PeerPort => KGS::Protocol::KGSPORT
96 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
105 $sock->blocking (1);
106 $self->{protocol}->handshake ($sock);
107 $sock->blocking (0);
108
109 $self->listen ($self->{protocol});
110 $self->{roomlist}->listen ($self->{protocol});
111 #$self->{gamelist}->listen ($conn);
112
113 # now login
114 $ENV{KGSUEME_CLIENTVER} ||= "1.4.2_03:Swing app:Sun Microsystems Inc."; # he asked for it...#d#
115 $self->{protocol}->login ($ENV{KGSUEME_CLIENTVER} || "kgsueme $VERSION $^O", # allow users to overwrite
116 $self->{login}->get_text,
117 $self->{password}->get_text,
118 "en_US");
119
120 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 $self->{protocol}->feed_data ($buf);
126 } elsif (defined $len || (!$!{EINTR} and !$!{EAGAIN})) {
127 remove Glib::Source $input;
128 $self->{protocol}->disconnect;
129 warn "disconnect: $!";#TODO#
130 app::status ("disconnect", "$!");#TODO# not visible
131 }
132 1;
133 }, G_PRIORITY_HIGH;
134 }
135
136 sub inject_login {
137 my ($self, $msg) = @_;
138
139 $self->{name} = $self->{conn}{name};
140
141 $::config->{login} = $self->{name};
142
143 app::status("login", "logged in as '$self->{name}' with status '$msg->{message}' ('$msg->{reason}')");
144
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 sub listen {
158 my $self = shift;
159
160 $self->SUPER::listen (@_, qw(login userpic idle_warn msg_chat chal_defaults upd_rooms));
161 }
162
163 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 sound::play 2, "ring";
176 }
177 }
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 $self->open_room (%$_) if $_->is_default;#d#
188 }
189 }
190
191 sub inject_chal_defaults {
192 my ($self, $msg) = @_;
193
194 $self->{defaults} = $msg->{defaults};
195 }
196
197 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 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 }
241
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 Scalar::Util::weaken $self->{game}{$arg{channel}};
248 $self->{game}{$arg{channel}};
249 }
250
251 sub open_room {
252 my ($self, %arg) = @_;
253
254 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 Scalar::Util::weaken $self->{room}{$arg{channel}};
261
262 $room->join;
263 $self->{room}{$arg{channel}};
264 }
265
266 sub open_user {
267 my ($self, %arg) = @_;
268
269 ($self->{user}{lc $arg{name}} ||= new user %arg, conn => $self->{conn}, app => $self)
270 ->join;
271 Scalar::Util::weaken $self->{user}{lc $arg{name}};
272 $self->{user}{lc $arg{name}};
273 }
274
275 sub do_command {
276 my ($self, $chat, $cmd, $arg, %arg) = @_;
277
278 if ($cmd eq "say") {
279 if (my $context = $arg{game} || $arg{room} || $arg{user}) {
280 $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 }
290
291 1;
292