ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC.pm
(Generate patch)

Comparing deliantra/Deliantra-Client/DC.pm (file contents):
Revision 1.82 by root, Wed May 31 07:13:50 2006 UTC vs.
Revision 1.110 by root, Sun Jul 30 17:43:26 2006 UTC

1=head1 NAME 1=head1 NAME
2 2
3CFClient - undocumented utility garbage for our crossfire client 3CFPlus - undocumented utility garbage for our crossfire client
4 4
5=head1 SYNOPSIS 5=head1 SYNOPSIS
6 6
7 use CFClient; 7 use CFPlus;
8 8
9=head1 DESCRIPTION 9=head1 DESCRIPTION
10 10
11=over 4 11=over 4
12 12
13=cut 13=cut
14 14
15package CFClient; 15package CFPlus;
16 16
17BEGIN { 17BEGIN {
18 $VERSION = '0.1'; 18 $VERSION = '0.1';
19 19
20 use XSLoader; 20 use XSLoader;
21 XSLoader::load "CFClient", $VERSION; 21 XSLoader::load "CFPlus", $VERSION;
22} 22}
23 23
24use utf8; 24use utf8;
25 25
26use Carp (); 26use Carp ();
27use AnyEvent (); 27use AnyEvent ();
28use BerkeleyDB; 28use BerkeleyDB;
29use Pod::POM ();
30use Scalar::Util ();
31use Storable (); # finally
32
33our %STAT_TOOLTIP = (
34 Str => "<b>Physical Strength</b>, determines damage dealt with weapons, how much you can carry, and how often you can attack",
35 Dex => "<b>Dexterity</b>, your physical agility. Determines chance of being hit and affects armor class and speed",
36 Con => "<b>Constitution</b>, physical health and toughness. Determines how many healthpoints you can have",
37 Int => "<b>Intelligence</b>, your ability to learn and use skills and incantations (both prayers and magic) and determines how much spell points you can have",
38 Wis => "<b>Wisdom</b>, the ability to learn and use divine magic (prayers). Determines how many grace points you can have",
39 Pow => "<b>Power</b>, your magical potential. Influences the strength of spell effects, and also how much your spell and grace points increase when leveling up",
40 Cha => "<b>Charisma</b>, how well you are received by NPCs. Affects buying and selling prices in shops.",
41
42 Wc => "<b>Weapon Class</b>, effectiveness of melee/missile attacks. Lower is more potent. Current weapon, level and Str are some things which effect the value of Wc. The value of Wc may range between 25 and -72.",
43 Ac => "<b>Armour Class</b>, how protected you are from being hit by any attack. Lower values are better. Ac is based on your race and is modified by the Dex and current armour worn. For characters that cannot wear armour, Ac improves as their level increases.",
44 Dam => "<b>Damage</b>, how much damage your melee/missile attack inflicts. Higher values indicate a greater amount of damage will be inflicted with each attack.",
45 Arm => "<b>Armour</b>, how much damage (from physical attacks) will be subtracted from successful hits made upon you. This value ranges between 0 to 99%. Current armour worn primarily determines Arm value. This is the same as the physical resistance.",
46 Spd => "<b>Speed</b>, how fast you can move. The value of speed may range between nearly 0 (\"very slow\") to higher than 5 (\"lightning fast\"). Base speed is determined from the Dex and modified downward proportionally by the amount of weight carried which exceeds the Max Carry limit. The armour worn also sets the upper limit on speed.",
47 WSp => "<b>Weapon Speed</b>, how many attacks you may make per unit of time (0.120s). Higher values indicate faster attack speed. Current weapon and Dex effect the value of weapon speed.",
48);
49
50=item guard { BLOCK }
51
52Returns an object that executes the given block as soon as it is destroyed.
53
54=cut
55
56sub guard(&) {
57 bless \(my $cb = $_[0]), "CFPlus::Guard"
58}
59
60sub CFPlus::Guard::DESTROY {
61 ${$_[0]}->()
62}
63
64sub asxml($) {
65 local $_ = $_[0];
66
67 s/&/&amp;/g;
68 s/>/&gt;/g;
69 s/</&lt;/g;
70
71 $_
72}
73
74package CFPlus::Database;
75
76our @ISA = BerkeleyDB::Btree::;
77
78sub get($$) {
79 my $data;
80
81 $_[0]->db_get ($_[1], $data) == 0
82 ? $data
83 : ()
84}
85
86my %DB_SYNC;
87
88sub put($$$) {
89 my ($db, $key, $data) = @_;
90
91 $DB_SYNC{$db} = AnyEvent->timer (after => 5, cb => sub { $db->db_sync });
92
93 $db->db_put ($key => $data)
94}
95
96package CFPlus;
29 97
30sub find_rcfile($) { 98sub find_rcfile($) {
31 my $path; 99 my $path;
32 100
33 for (grep !ref, @INC) { 101 for (grep !ref, @INC) {
34 $path = "$_/CFClient/resources/$_[0]"; 102 $path = "$_/CFPlus/resources/$_[0]";
35 return $path if -r $path; 103 return $path if -r $path;
36 } 104 }
37 105
38 die "FATAL: can't find required file $_[0]\n"; 106 die "FATAL: can't find required file $_[0]\n";
39} 107}
40 108
109BEGIN {
110 use Crossfire::Protocol::Base ();
111 *to_json = \&Crossfire::Protocol::Base::to_json;
112 *from_json = \&Crossfire::Protocol::Base::from_json;
113}
114
41sub read_cfg { 115sub read_cfg {
42 my ($file) = @_; 116 my ($file) = @_;
43 117
44 open CFG, $file 118 open my $fh, $file
45 or return; 119 or return;
46 120
47 my $CFG;
48
49 local $/; 121 local $/;
50 $CFG = eval <CFG>; 122 my $CFG = <$fh>;
51 123
52 $::CFG = $CFG; 124 if ($CFG =~ /^---/) { ## TODO compatibility cruft, remove
53 125 require YAML;
54 close CFG; 126 utf8::decode $CFG;
127 $::CFG = YAML::Load ($CFG);
128 } elsif ($CFG =~ /^\{/) {
129 $::CFG = from_json $CFG;
130 } else {
131 $::CFG = eval $CFG; ## todo comaptibility cruft
132 }
55} 133}
56 134
57sub write_cfg { 135sub write_cfg {
58 my ($file) = @_; 136 my ($file) = @_;
59 137
60 open CFG, ">$file" 138 $::CFG->{VERSION} = $::VERSION;
139
140 open my $fh, ">:utf8", $file
61 or return; 141 or return;
62 142 print $fh to_json $::CFG;
63 {
64 require Data::Dumper;
65 local $Data::Dumper::Purity = 1;
66 $::CFG->{VERSION} = $::VERSION;
67 print CFG Data::Dumper->Dump ([$::CFG], [qw/CFG/]);
68 }
69
70 close CFG;
71} 143}
72
73mkdir "$Crossfire::VARDIR/cfplus", 0777;
74 144
75our $DB_ENV; 145our $DB_ENV;
76 146
77{ 147{
78 use strict; 148 use strict;
79 149
150 mkdir "$Crossfire::VARDIR/cfplus", 0777;
80 my $recover = $BerkeleyDB::db_version >= 4.4 151 my $recover = $BerkeleyDB::db_version >= 4.4
81 ? eval "DB_REGISTER | DB_RECOVER" 152 ? eval "DB_REGISTER | DB_RECOVER"
82 : 0; 153 : 0;
83 warn $recover;
84 154
85 $DB_ENV = new BerkeleyDB::Env 155 $DB_ENV = new BerkeleyDB::Env
86 -Home => "$Crossfire::VARDIR/cfplus", 156 -Home => "$Crossfire::VARDIR/cfplus",
87 -Cachesize => 1_000_000, 157 -Cachesize => 1_000_000,
88 -ErrFile => "$Crossfire::VARDIR/cfplus/errorlog.txt", 158 -ErrFile => "$Crossfire::VARDIR/cfplus/errorlog.txt",
96sub db_table($) { 166sub db_table($) {
97 my ($table) = @_; 167 my ($table) = @_;
98 168
99 $table =~ s/([^a-zA-Z0-9_\-])/sprintf "=%x=", ord $1/ge; 169 $table =~ s/([^a-zA-Z0-9_\-])/sprintf "=%x=", ord $1/ge;
100 170
101 new CFClient::Database 171 new CFPlus::Database
102 -Env => $DB_ENV, 172 -Env => $DB_ENV,
103 -Filename => $table, 173 -Filename => $table,
104# -Filename => "database", 174# -Filename => "database",
105# -Subname => $table, 175# -Subname => $table,
106 -Property => DB_CHKSUM, 176 -Property => DB_CHKSUM,
107 -Flags => DB_CREATE | DB_UPGRADE, 177 -Flags => DB_CREATE | DB_UPGRADE,
108 or die "unable to create/open database table $_[0]: $BerkeleyDB::Error" 178 or die "unable to create/open database table $_[0]: $BerkeleyDB::Error"
109} 179}
110 180
111sub pod_to_pango($) { 181package CFPlus::Layout;
112 my ($pom) = @_;
113 182
114 $pom->present ("CFClient::PodToPango") 183$CFPlus::OpenGL::SHUTDOWN_HOOK{"CFPlus::Layout"} = sub {
115} 184 reset_glyph_cache;
116
117sub pod_to_pango_list($) {
118 my ($pom) = @_;
119
120 [
121 map s/^(\s*)// && [40 * length $1, length $_ ? $_ : " "],
122 split /\n/, $pom->present ("CFClient::PodToPango")
123 ]
124}
125
126package CFClient::PodToPango;
127
128use base Pod::POM::View::Text;
129
130our $indent = 0;
131
132*view_seq_code =
133*view_seq_bold = sub { "<b>$_[1]</b>" };
134*view_seq_italic = sub { "<i>$_[1]</i>" };
135*view_seq_space =
136*view_seq_link =
137*view_seq_index = sub { CFClient::UI::Label::escape ($_[1]) };
138
139sub view_seq_text {
140 my $text = $_[1];
141 $text =~ s/\s+/ /g;
142 CFClient::UI::Label::escape ($text)
143}
144
145sub view_item {
146 ("\t" x ($indent / 4))
147 . $_[1]->title->present ($_[0])
148 . "\n"
149 . $_[1]->content->present ($_[0])
150}
151
152sub view_verbatim {
153 (join "",
154 map +("\t" x ($indent / 2)) . "<tt>$_</tt>\n",
155 split /\n/, CFClient::UI::Label::escape ($_[1]))
156 . "\n"
157}
158
159sub view_textblock {
160 ("\t" x ($indent / 2)) . "$_[1]\n\n"
161}
162
163sub view_head1 {
164 "\n\n<span foreground='#ffff00' size='x-large'>" . $_[1]->title->present ($_[0]) . "</span>\n\n"
165 . $_[1]->content->present ($_[0])
166}; 185};
167 186
168sub view_head2 {
169 "\n<span foreground='#ccccff' size='large'>" . $_[1]->title->present ($_[0]) . "</span>\n\n"
170 . $_[1]->content->present ($_[0])
171};
172
173sub view_head3 {
174 "\n<span size='large'>" . $_[1]->title->present ($_[0]) . "</span>\n\n"
175 . $_[1]->content->present ($_[0])
176};
177
178sub view_over {
179 local $indent = $indent + $_[1]->indent;
180 $_[1]->content->present ($_[0])
181}
182
183package CFClient::Database;
184
185our @ISA = BerkeleyDB::Btree::;
186
187sub get($$) {
188 my $data;
189
190 $_[0]->db_get ($_[1], $data) == 0
191 ? $data
192 : ()
193}
194
195my %DB_SYNC;
196
197sub put($$$) {
198 my ($db, $key, $data) = @_;
199
200 $DB_SYNC{$db} = AnyEvent->timer (after => 5, cb => sub { $db->db_sync });
201
202 $db->db_put ($key => $data)
203}
204
205package CFClient::Item; 187package CFPlus::Item;
206 188
207use strict; 189use strict;
208use Crossfire::Protocol::Constants; 190use Crossfire::Protocol::Constants;
191
192my $last_enter_count = 1;
209 193
210sub desc_string { 194sub desc_string {
211 my ($self) = @_; 195 my ($self) = @_;
212 196
213 my $desc = 197 my $desc =
239 my $weight = ($self->{nrof} || 1) * $self->{weight}; 223 my $weight = ($self->{nrof} || 1) * $self->{weight};
240 224
241 $weight < 0 ? "?" : $weight * 0.001 225 $weight < 0 ? "?" : $weight * 0.001
242} 226}
243 227
228sub do_n_dialog {
229 my ($cb) = @_;
230
231 my $w = new CFPlus::UI::FancyFrame
232 on_delete => sub { $_[0]->destroy; 1 },
233 has_close_button => 1,
234 ;
235
236 $w->add (my $vb = new CFPlus::UI::VBox x => "center", y => "center");
237 $vb->add (new CFPlus::UI::Label text => "Enter item count:");
238 $vb->add (my $entry = new CFPlus::UI::Entry
239 text => $last_enter_count,
240 on_activate => sub {
241 my ($entry) = @_;
242 $last_enter_count = $entry->get_text;
243 $cb->($last_enter_count);
244 $w->hide;
245 $w->destroy;
246
247 0
248 },
249 on_escape => sub { $w->destroy; 1 },
250 );
251 $entry->grab_focus;
252 $w->show;
253}
254
244sub update_widgets { 255sub update_widgets {
245 my ($self) = @_; 256 my ($self) = @_;
246 257
258 # necessary to avoid cyclic references
259 Scalar::Util::weaken $self;
260
247 my $button_cb = sub { 261 my $button_cb = sub {
248 my (undef, $ev, $x, $y) = @_; 262 my (undef, $ev, $x, $y) = @_;
249 263
250 if (($ev->{mod} & CFClient::KMOD_SHIFT) && $ev->{button} == 1) {
251 my $targ = $::CONN->{player}{tag}; 264 my $targ = $::CONN->{player}{tag};
252 265
253 if ($self->{container} == $::CONN->{player}{tag}) { 266 if ($self->{container} == $::CONN->{player}{tag}) {
254 $targ = $::CONN->{open_container}; 267 $targ = $::CONN->{open_container};
255 } 268 }
256 269
270 if (($ev->{mod} & CFPlus::KMOD_SHIFT) && $ev->{button} == 1) {
257 $::CONN->send ("move $targ $self->{tag} 0") 271 $::CONN->send ("move $targ $self->{tag} 0")
258 if $targ || !($self->{flags} & F_LOCKED); 272 if $targ || !($self->{flags} & F_LOCKED);
273 } elsif (($ev->{mod} & CFPlus::KMOD_SHIFT) && $ev->{button} == 2) {
274 $self->{flags} & F_LOCKED
275 ? $::CONN->send ("lock " . pack "CN", 0, $self->{tag})
276 : $::CONN->send ("lock " . pack "CN", 1, $self->{tag})
259 } elsif ($ev->{button} == 1) { 277 } elsif ($ev->{button} == 1) {
260 $::CONN->send ("examine $self->{tag}"); 278 $::CONN->send ("examine $self->{tag}");
261 } elsif ($ev->{button} == 2) { 279 } elsif ($ev->{button} == 2) {
262 $::CONN->send ("apply $self->{tag}"); 280 $::CONN->send ("apply $self->{tag}");
263 } elsif ($ev->{button} == 3) { 281 } elsif ($ev->{button} == 3) {
282 my $move_prefix = $::CONN->{open_container} ? 'put' : 'drop';
283 if ($self->{container} == $::CONN->{open_container}) {
284 $move_prefix = "take";
285 }
286
264 my @menu_items = ( 287 my @menu_items = (
265 ["examine", sub { $::CONN->send ("examine $self->{tag}") }], 288 ["examine", sub { $::CONN->send ("examine $self->{tag}") }],
266 ["mark", sub { $::CONN->send ("mark ". pack "N", $self->{tag}) }], 289 ["mark", sub { $::CONN->send ("mark ". pack "N", $self->{tag}) }],
290 ["ignite/thaw", # first try of an easier use of flint&steel
291 sub {
292 $::CONN->send ("mark ". pack "N", $self->{tag});
293 $::CONN->send ("command apply flint and steel");
294 }
295 ],
296 ["inscribe", # first try of an easier use of flint&steel
297 sub {
298 &::open_string_query ("Text to inscribe", sub {
299 my ($entry, $txt) = @_;
300 $::CONN->send ("mark ". pack "N", $self->{tag});
301 $::CONN->send ("command use_skill inscription $txt");
302 });
303 }
304 ],
267 ["apply", sub { $::CONN->send ("apply $self->{tag}") }], 305 ["apply", sub { $::CONN->send ("apply $self->{tag}") }],
268 ( 306 (
269 $self->{flags} & F_LOCKED 307 $self->{flags} & F_LOCKED
270 ? ( 308 ? (
271 ["unlock", sub { $::CONN->send ("lock " . pack "CN", 0, $self->{tag}) }], 309 ["unlock", sub { $::CONN->send ("lock " . pack "CN", 0, $self->{tag}) }],
272 ) 310 )
273 : ( 311 : (
274 ["lock", sub { $::CONN->send ("lock " . pack "CN", 1, $self->{tag}) }], 312 ["lock", sub { $::CONN->send ("lock " . pack "CN", 1, $self->{tag}) }],
275 ["drop", sub { $::CONN->send ("move $::CONN->{open_container} $self->{tag} 0") }], 313 ["$move_prefix all", sub { $::CONN->send ("move $targ $self->{tag} 0") }],
314 ["$move_prefix &lt;n&gt;",
315 sub {
316 do_n_dialog (sub { $::CONN->send ("move $targ $self->{tag} $_[0]") })
317 }
318 ]
276 ) 319 )
277 ), 320 ),
278 ); 321 );
279 322
280 CFClient::UI::Menu->new (items => \@menu_items)->popup ($ev); 323 CFPlus::UI::Menu->new (items => \@menu_items)->popup ($ev);
281 } 324 }
282 325
283 1 326 1
284 }; 327 };
285 328
286 my $tooltip_std = "<small>" 329 my $tooltip_std = "<small>"
287 . "Left click - examine item\n" 330 . "Left click - examine item\n"
288 . "Shift-Left click - " . ($self->{container} ? "move or drop" : "take") . " item\n" 331 . "Shift-Left click - " . ($self->{container} ? "move or drop" : "take") . " item\n"
289 . "Middle click - apply\n" 332 . "Middle click - apply\n"
333 . "Shift-Middle click - lock/unlock\n"
290 . "Right click - further options" 334 . "Right click - further options"
291 . "</small>\n"; 335 . "</small>\n";
292 336
337 my $bg = $self->{flags} & F_CURSED ? [1 , 0 , 0, 0.5]
338 : $self->{flags} & F_MAGIC ? [0.2, 0.2, 1, 0.5]
339 : undef;
340
293 $self->{face_widget} ||= new CFClient::UI::Face 341 $self->{face_widget} ||= new CFPlus::UI::Face
294 can_events => 1, 342 can_events => 1,
295 can_hover => 1, 343 can_hover => 1,
296 anim => $self->{anim}, 344 anim => $self->{anim},
297 animspeed => $self->{animspeed}, # TODO# must be set at creation time 345 animspeed => $self->{animspeed}, # TODO# must be set at creation time
298 on_button_down => $button_cb, 346 on_button_down => $button_cb,
299 ; 347 ;
348 $self->{face_widget}{bg} = $bg;
300 $self->{face_widget}{face} = $self->{face}; 349 $self->{face_widget}{face} = $self->{face};
301 $self->{face_widget}{anim} = $self->{anim}; 350 $self->{face_widget}{anim} = $self->{anim};
302 $self->{face_widget}{animspeed} = $self->{animspeed}; 351 $self->{face_widget}{animspeed} = $self->{animspeed};
303 $self->{face_widget}->set_tooltip ( 352 $self->{face_widget}->set_tooltip (
304 "<b>Face/Animation.</b>\n" 353 "<b>Face/Animation.</b>\n"
305 . "Item uses face #$self->{face}. " 354 . "Item uses face #$self->{face}. "
306 . ($self->{animspeed} ? "Item uses animation #$self->{anim} at " . (1 / $self->{animspeed}) . "fps. " : "Item is not animated. ") 355 . ($self->{animspeed} ? "Item uses animation #$self->{anim} at " . (1 / $self->{animspeed}) . "fps. " : "Item is not animated. ")
307 . "\n\n$tooltip_std" 356 . "\n\n$tooltip_std"
308 ); 357 );
309 358
310 $self->{desc_widget} ||= new CFClient::UI::Label 359 $self->{desc_widget} ||= new CFPlus::UI::Label
311 can_events => 1, 360 can_events => 1,
312 can_hover => 1, 361 can_hover => 1,
313 ellipsise => 2, 362 ellipsise => 2,
314 align => -1, 363 align => -1,
315 on_button_down => $button_cb, 364 on_button_down => $button_cb,
316 ; 365 ;
317 my $desc = CFClient::Item::desc_string $self; 366 my $desc = CFPlus::Item::desc_string $self;
367 $self->{desc_widget}{bg} = $bg;
318 $self->{desc_widget}->set_text ($desc); 368 $self->{desc_widget}->set_text ($desc);
319 $self->{desc_widget}->set_tooltip ("<b>$desc</b>.\n$tooltip_std"); 369 $self->{desc_widget}->set_tooltip ("<b>$desc</b>.\n$tooltip_std");
320 370
321 $self->{weight_widget} ||= new CFClient::UI::Label 371 $self->{weight_widget} ||= new CFPlus::UI::Label
322 can_events => 1, 372 can_events => 1,
323 can_hover => 1, 373 can_hover => 1,
324 ellipsise => 0, 374 ellipsise => 0,
325 align => 0, 375 align => 0,
326 on_button_down => $button_cb, 376 on_button_down => $button_cb,
327 ; 377 ;
378 $self->{weight_widget}{bg} = $bg;
328 $self->{weight_widget}->set_text (CFClient::Item::weight_string $self); 379 $self->{weight_widget}->set_text (CFPlus::Item::weight_string $self);
329
330 $self->{weight_widget}->set_tooltip ( 380 $self->{weight_widget}->set_tooltip (
331 "<b>Weight</b>.\n" 381 "<b>Weight</b>.\n"
332 . ($self->{weight} >= 0 ? "One item weighs $self->{weight}g. " : "You have no idea how much this weighs. ") 382 . ($self->{weight} >= 0 ? "One item weighs $self->{weight}g. " : "You have no idea how much this weighs. ")
333 . ($self->{nrof} ? "You have $self->{nrof} of it. " : "Item cannot stack with others of it's kind. ") 383 . ($self->{nrof} ? "You have $self->{nrof} of it. " : "Item cannot stack with others of it's kind. ")
334 . "\n\n$tooltip_std" 384 . "\n\n$tooltip_std"
335 ); 385 );
336} 386}
337 387
338package CFClient::Binder;
339
340my @ALLOWED_MODIFIER_KEYS = (
341 (CFClient::SDLK_LSHIFT),
342 (CFClient::SDLK_LCTRL ),
343 (CFClient::SDLK_LALT ),
344 (CFClient::SDLK_LMETA ),
345
346 (CFClient::SDLK_RSHIFT),
347 (CFClient::SDLK_RCTRL ),
348 (CFClient::SDLK_RALT ),
349 (CFClient::SDLK_RMETA ),
350);
351
352my %ALLOWED_MODIFIERS = (
353 (CFClient::KMOD_LSHIFT) => "LSHIFT",
354 (CFClient::KMOD_LCTRL ) => "LCTRL",
355 (CFClient::KMOD_LALT ) => "LALT",
356 (CFClient::KMOD_LMETA ) => "LMETA",
357
358 (CFClient::KMOD_RSHIFT) => "RSHIFT",
359 (CFClient::KMOD_RCTRL ) => "RCTRL",
360 (CFClient::KMOD_RALT ) => "RALT",
361 (CFClient::KMOD_RMETA ) => "RMETA",
362);
363
364my %DIRECT_BIND_CHARS = map { $_ => 1 } qw/0 1 2 3 4 5 6 7 8 9/;
365my @DIRECT_BIND_KEYS = (
366 CFClient::SDLK_F1,
367 CFClient::SDLK_F2,
368 CFClient::SDLK_F3,
369 CFClient::SDLK_F4,
370 CFClient::SDLK_F5,
371 CFClient::SDLK_F6,
372 CFClient::SDLK_F7,
373 CFClient::SDLK_F8,
374 CFClient::SDLK_F9,
375 CFClient::SDLK_F10,
376 CFClient::SDLK_F11,
377 CFClient::SDLK_F12,
378 CFClient::SDLK_F13,
379 CFClient::SDLK_F14,
380 CFClient::SDLK_F15,
381);
382
383# this binding dialog asks for a key-combo to be pressed
384# and if successful it calls the $cb with $mod and $sym as args.
385sub open_binding_dialog {
386 my ($cb) = @_;
387
388 my $w = new CFClient::UI::FancyFrame
389 title => "Bind Action",
390 x => "center",
391 y => "center";
392
393 $w->add (my $vb = new CFClient::UI::VBox);
394 $vb->add (new CFClient::UI::Label
395 text => "Press a modifier (CTRL, ALT and/or SHIFT) and a key."
396 ."You can only bind 0-9 and F1-F15 without modifiers."
397 );
398 $vb->add (my $entry = new CFClient::UI::Entry
399 text => "",
400 on_key_down => sub {
401 my ($entry, $ev) = @_;
402
403 my $mod = $ev->{mod};
404 my $sym = $ev->{sym};
405
406 # XXX: This seems a little bit hackisch to me, but i have to ignore them
407 if (grep { $_ == $sym } @ALLOWED_MODIFIER_KEYS) {
408 return;
409 }
410
411 if ($mod == CFClient::KMOD_NONE
412 and not $DIRECT_BIND_CHARS{chr ($ev->{unicode})}
413 and not grep { $sym == $_ } @DIRECT_BIND_KEYS)
414 {
415 $::STATUSBOX->add (
416 "Can't bind key ".CFClient::SDL_GetKeyName ($sym)
417 ." directly without modifier! It would damage the completer handling."
418 );
419 return;
420 }
421
422 $entry->focus_out;
423
424 $cb->($mod, $sym);
425
426 $w->destroy
427 });
428
429 $entry->focus_in;
430 $w->show;
431}
432
433sub keycombo_to_name {
434 my ($mod, $sym) = @_;
435
436 my $mods = join '+',
437 map { $ALLOWED_MODIFIERS{$_} }
438 grep { ($_ + 0) & ($mod + 0) }
439 keys %ALLOWED_MODIFIERS;
440 $mods .= "+" if $mods ne '';
441
442 return $mods . CFClient::SDL_GetKeyName ($sym);
443}
444
4451; 3881;
446 389
447=back 390=back
448 391
449=head1 AUTHOR 392=head1 AUTHOR

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines