ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/kgsueme/kgsueme/app.pl
Revision: 1.19
Committed: Mon May 31 17:15:05 2004 UTC (20 years ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.18: +2 -2 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 $self = $self->Glib::Object::new;
26 $self->{$_} = delete $arg{$_} for keys %arg;
27
28 Scalar::Util::weaken ($::self = $self); # singleton...
29
30 $self->{conn} = new KGS::Protocol::Client;
31
32 $self->listen ($self->{conn}, qw(login userpic idle_warn msg_chat chal_defaults));
33
34 $self->{roomlist} = new roomlist conn => $self->{conn}, app => $self;
35
36 $self->set_title ('kgsueme');
37 gtk::state $self, "main::window", undef, window_size => [400, 580];
38 $self->signal_connect (destroy => sub { %{$_[0]} = () });
39 $self->signal_connect (delete_event => sub { main_quit Gtk2; 1 });
40
41 $self->add (my $vbox = new Gtk2::VBox);
42
43 $vbox->pack_start (($buttonbox = new Gtk2::HButtonBox), 0, 1, 0);
44 $buttonbox->set_spacing (0);
45
46 my $button = sub {
47 $buttonbox->add (my $button = new Gtk2::Button $_[0]);
48 signal_connect $button clicked => $_[1];
49 };
50
51 $button->("Login", sub { $self->login; });
52 $button->("Roomlist", sub { $self->{roomlist}->show; });
53 $button->("Save Config & Layout", \&util::save_config);
54 $button->("Quit", sub { main_quit Gtk2 });
55
56 $vbox->pack_start ((my $hbox = new Gtk2::HBox), 0, 1, 0);
57
58 $hbox->add (new Gtk2::Label "Login");
59
60 $hbox->add ($self->{login} = new_with_max_length Gtk2::Entry 12);
61 $self->{login}->set_text ($::config->{login});
62
63 $hbox->add (new Gtk2::Label "Password");
64 $hbox->add ($self->{password} = new Gtk2::Entry);
65 $self->{password}->set (visibility => 0, is_focus => 1);
66 $self->{password}->signal_connect (activate => sub { $self->login });
67
68 $vbox->pack_start ((my $vpane = new Gtk2::VPaned), 1, 1, 0);
69 $vpane->set (position_set => 1);
70 gtk::state $vpane, "main::vpane", undef, position => 400;
71
72 $vbox->pack_start(($self->{status} = new Gtk2::Statusbar), 0, 1, 0);
73
74 $self->{gamelist} = new gamelist conn => $self->{conn}, app => $self;
75 $vpane->pack1 ($self->{gamelist}, 1, 1);
76
77 $self->{rooms} = new Gtk2::Notebook;
78 $self->{rooms}->set (enable_popup => 1, scrollable => 1);
79
80 $vpane->pack2 ($self->{rooms}, 1, 1);
81
82 $self->show_all;
83
84 $self;
85 }
86
87 sub login {
88 my ($self) = @_;
89
90 $self->{conn}->disconnect;
91
92 # initialize new socket and connection
93 my $sock = new IO::Socket::INET PeerHost => KGS::Protocol::KGSHOST, PeerPort => KGS::Protocol::KGSPORT
94 or die "connect: $!";
95
96 $sock->blocking (1);
97 $self->{conn}->handshake ($sock);
98 $sock->blocking (0);
99
100 my $input; $input = add_watch Glib::IO fileno $sock, [G_IO_IN, G_IO_ERR, G_IO_HUP], sub {
101 # this is dorked
102 my $buf;
103 my $len = sysread $sock, $buf, 16384;
104 if ($len) {
105 $self->{conn}->feed_data ($buf);
106 } elsif (defined $len || (!$!{EINTR} and !$!{EAGAIN})) {
107 warn "disconnected";#d#
108 remove Glib::Source $input;
109 $self->event_disconnect;
110 }
111 1;
112 }, G_PRIORITY_HIGH;
113
114 # now login
115 $ENV{KGSUEME_CLIENTVER} = "1.4.1_01:Swing app:Sun Microsystems Inc."; # he asked for it...#d#
116 $self->{conn}->login($ENV{KGSUEME_CLIENTVER} || "kgsueme $VERSION $^O", # allow users to overwrite
117 $self->{login}->get_text,
118 $self->{password}->get_text);
119 }
120
121 sub inject_login {
122 my ($self, $msg) = @_;
123
124 $self->{name} = $self->{conn}{name};
125
126 $::config->{login} = $self->{name};
127
128 app::status("login", "logged in as '$self->{name}' with status '$msg->{message}' ('$msg->{reason}')");
129
130 if ($msg->{success}) {
131 # auto-join
132 $self->open_room (%$_) for values %{$::config->{rooms}};
133
134 sound::play 3, "connect";
135 } elsif ($msg->{result} eq "user unknown") {
136 sound::play 2, "user_unknown";
137 } else {
138 sound::play 2, "warning";
139 }
140 }
141
142 sub inject_idle_warn {
143 my ($self, $msg) = @_;
144
145 $self->send ("idle_reset");
146 }
147
148 sub inject_msg_chat {
149 my ($self, $msg) = @_;
150
151 if ((lc $msg->{name2}) eq (lc $self->{name})) {
152 unless ($self->{user}{lc $msg->{name}}) {
153 $self->open_user (name => $msg->{name})->inject ($msg);
154 sound::play 2, "ring";
155 }
156 }
157 }
158
159 sub inject_chal_defaults {
160 my ($self, $msg) = @_;
161
162 $self->{defaults} = $msg->{defaults};
163 }
164
165 my %userpic;
166 my %userpic_cb;
167
168 # static method to request the userimage and call the cb when it's available.
169 sub userpic {
170 my ($self, $name, $cb) = @_;
171 $self->get_userpic ($name, $cb);
172 }
173
174 sub get_userpic {
175 my ($self, $name, $cb) = @_;
176
177 if (exists $userpic{$name}) {
178 $cb->($userpic{$name});
179 } else {
180 if (!exists $userpic_cb{$name}) {
181 # after 10 seconds, flush callbacks
182 $self->send (req_pic => name => $name);
183 add Glib::Timeout 10000, sub {
184 $_->() for @{delete $userpic_cb{$name} || []};
185 0;
186 };
187 }
188 push @{$userpic_cb{$name}}, $cb;
189 }
190 }
191
192 sub inject_userpic {
193 my ($self, $msg) = @_;
194
195 $userpic{$msg->{name}} = $msg->{data};
196 $_->($userpic{$msg->{name}}) for @{delete $userpic_cb{$msg->{name}} || []};
197 }
198
199 sub event_disconnect {
200 $_->destroy
201 for values %{delete $self->{game} or {}}, values %{delete $self->{room} or {}};
202 }
203
204 sub open_game {
205 my ($self, %arg) = @_;
206
207 ($self->{game}{$arg{channel}} ||= new game %arg, conn => $self->{conn}, app => $self)
208 ->join;
209 Scalar::Util::weaken $self->{game}{$arg{channel}};
210 $self->{game}{$arg{channel}};
211 }
212
213 sub open_room {
214 my ($self, %arg) = @_;
215
216 my $room = $self->{room}{$arg{channel}} ||= do {
217 my $room = new room %arg, conn => $self->{conn}, app => $self;
218 $room->show_all;
219 $self->{rooms}->append_page ($room, new Gtk2::Label $room->{name});
220 $room;
221 };
222 Scalar::Util::weaken $self->{room}{$arg{channel}};
223
224 $room->join;
225 $self->{room}{$arg{channel}};
226 }
227
228 sub open_user {
229 my ($self, %arg) = @_;
230
231 ($self->{user}{lc $arg{name}} ||= new user %arg, conn => $self->{conn}, app => $self)
232 ->join;
233 Scalar::Util::weaken $self->{user}{lc $arg{name}};
234 $self->{user}{lc $arg{name}};
235 }
236
237 sub do_command {
238 my ($self, $chat, $cmd, $arg, %arg) = @_;
239
240 if ($cmd eq "say") {
241 if (my $context = $arg{game} || $arg{room} || $arg{user}) {
242 $context->say ($arg);
243 } else {
244 $chat->append_text ("\n<error>no context for message</error>");
245 }
246 } elsif ($cmd eq "whois" or $cmd eq "w") {
247 $self->open_user (name => $arg);
248 } else {
249 $chat->append_text ("\n<error>unknown command</error>");
250 }
251 }
252
253 1;
254