ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC.pm
Revision: 1.97
Committed: Tue Jul 4 23:23:31 2006 UTC (17 years, 10 months ago) by root
Branch: MAIN
Changes since 1.96: +6 -0 lines
Log Message:
Get rid of cairo completely (yay!) and of ft2 factually (still need the
library as it included pangofc), by introducing a custom pango opengl
renderer.

Text rendering now no longer requires the distinction between rgba and
grayscale modes, requires much less texture space and memory, and is
faster on accelerated hardware (and possibly with software rendering, too).

All at the cost of only 1200 lines or so.

File Contents

# Content
1 =head1 NAME
2
3 CFClient - undocumented utility garbage for our crossfire client
4
5 =head1 SYNOPSIS
6
7 use CFClient;
8
9 =head1 DESCRIPTION
10
11 =over 4
12
13 =cut
14
15 package CFClient;
16
17 BEGIN {
18 $VERSION = '0.1';
19
20 use XSLoader;
21 XSLoader::load "CFClient", $VERSION;
22 }
23
24 use utf8;
25
26 use Carp ();
27 use AnyEvent ();
28 use BerkeleyDB;
29 use Pod::POM ();
30 use Scalar::Util ();
31 use Storable (); # finally
32
33 package CFClient::PodToPango;
34
35 use base Pod::POM::View::Text;
36
37 our $VERSION = 1; # bump if resultant formatting changes
38
39 our $indent = 0;
40
41 *view_seq_code =
42 *view_seq_bold = sub { "<b>$_[1]</b>" };
43 *view_seq_italic = sub { "<i>$_[1]</i>" };
44 *view_seq_space =
45 *view_seq_link =
46 *view_seq_index = sub { CFClient::UI::Label::escape ($_[1]) };
47
48 sub view_seq_text {
49 my $text = $_[1];
50 $text =~ s/\s+/ /g;
51 CFClient::UI::Label::escape ($text)
52 }
53
54 sub view_item {
55 ("\t" x ($indent / 4))
56 . $_[1]->title->present ($_[0])
57 . "\n\n"
58 . $_[1]->content->present ($_[0])
59 }
60
61 sub view_verbatim {
62 (join "",
63 map +("\t" x ($indent / 2)) . "<tt>$_</tt>\n",
64 split /\n/, CFClient::UI::Label::escape ($_[1]))
65 . "\n"
66 }
67
68 sub view_textblock {
69 ("\t" x ($indent / 2)) . "$_[1]\n\n"
70 }
71
72 sub view_head1 {
73 "\n\n<span foreground='#ffff00' size='x-large'>" . $_[1]->title->present ($_[0]) . "</span>\n\n"
74 . $_[1]->content->present ($_[0])
75 };
76
77 sub view_head2 {
78 "\n<span foreground='#ccccff' size='large'>" . $_[1]->title->present ($_[0]) . "</span>\n\n"
79 . $_[1]->content->present ($_[0])
80 };
81
82 sub view_head3 {
83 "\n<span size='large'>" . $_[1]->title->present ($_[0]) . "</span>\n\n"
84 . $_[1]->content->present ($_[0])
85 };
86
87 sub view_over {
88 local $indent = $indent + $_[1]->indent;
89 $_[1]->content->present ($_[0])
90 }
91
92 package CFClient::Database;
93
94 our @ISA = BerkeleyDB::Btree::;
95
96 sub get($$) {
97 my $data;
98
99 $_[0]->db_get ($_[1], $data) == 0
100 ? $data
101 : ()
102 }
103
104 my %DB_SYNC;
105
106 sub put($$$) {
107 my ($db, $key, $data) = @_;
108
109 $DB_SYNC{$db} = AnyEvent->timer (after => 5, cb => sub { $db->db_sync });
110
111 $db->db_put ($key => $data)
112 }
113
114 package CFClient;
115
116 sub find_rcfile($) {
117 my $path;
118
119 for (grep !ref, @INC) {
120 $path = "$_/CFClient/resources/$_[0]";
121 return $path if -r $path;
122 }
123
124 die "FATAL: can't find required file $_[0]\n";
125 }
126
127 sub read_cfg {
128 my ($file) = @_;
129
130 open CFG, $file
131 or return;
132
133 my $CFG;
134
135 local $/;
136 $CFG = eval <CFG>;
137
138 $::CFG = $CFG;
139
140 close CFG;
141 }
142
143 sub write_cfg {
144 my ($file) = @_;
145
146 open CFG, ">$file"
147 or return;
148
149 {
150 require Data::Dumper;
151 local $Data::Dumper::Purity = 1;
152 $::CFG->{VERSION} = $::VERSION;
153 print CFG Data::Dumper->Dump ([$::CFG], [qw/CFG/]);
154 }
155
156 close CFG;
157 }
158
159 our $DB_ENV;
160
161 {
162 use strict;
163
164 mkdir "$Crossfire::VARDIR/cfplus", 0777;
165 my $recover = $BerkeleyDB::db_version >= 4.4
166 ? eval "DB_REGISTER | DB_RECOVER"
167 : 0;
168
169 $DB_ENV = new BerkeleyDB::Env
170 -Home => "$Crossfire::VARDIR/cfplus",
171 -Cachesize => 1_000_000,
172 -ErrFile => "$Crossfire::VARDIR/cfplus/errorlog.txt",
173 # -ErrPrefix => "DATABASE",
174 -Verbose => 1,
175 -Flags => DB_CREATE | DB_RECOVER | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | $recover,
176 -SetFlags => DB_AUTO_COMMIT | DB_LOG_AUTOREMOVE,
177 or die "unable to create/open database home $Crossfire::VARDIR/cfplus: $BerkeleyDB::Error";
178 }
179
180 sub db_table($) {
181 my ($table) = @_;
182
183 $table =~ s/([^a-zA-Z0-9_\-])/sprintf "=%x=", ord $1/ge;
184
185 new CFClient::Database
186 -Env => $DB_ENV,
187 -Filename => $table,
188 # -Filename => "database",
189 # -Subname => $table,
190 -Property => DB_CHKSUM,
191 -Flags => DB_CREATE | DB_UPGRADE,
192 or die "unable to create/open database table $_[0]: $BerkeleyDB::Error"
193 }
194
195 my $pod_cache = db_table "pod_cache";
196
197 sub load_pod($$$$) {
198 my ($path, $filtertype, $filterversion, $filtercb) = @_;
199
200 stat $path
201 or die "$path: $!";
202
203 my $phash = join ",", $filterversion, $CFClient::PodToPango::VERSION, (stat _)[7,9];
204
205 my ($chash, $pom) = eval { @{ Storable::thaw $pod_cache->get ("$path/$filtertype") } };
206
207 return $pom if $chash eq $phash;
208
209 my $pod = do {
210 local $/;
211 open my $pod, "<:utf8", $_[0]
212 or die "$_[0]: $!";
213 <$pod>
214 };
215
216 #utf8::downgrade $pod;
217
218 $pom = $filtercb-> (Pod::POM->new->parse_text ($pod));
219
220 $pod_cache->put ("$path/$filtertype" => Storable::nfreeze [$phash, $pom]);
221
222 $pom
223 }
224
225 sub pod_to_pango($) {
226 my ($pom) = @_;
227
228 $pom->present ("CFClient::PodToPango")
229 }
230
231 sub pod_to_pango_list($) {
232 my ($pom) = @_;
233
234 [
235 map s/^(\s*)// && [40 * length $1, length $_ ? $_ : " "],
236 split /\n/, $pom->present ("CFClient::PodToPango")
237 ]
238 }
239
240 package CFClient::Layout;
241
242 $CFClient::OpenGL::SHUTDOWN_HOOK{"CFClient::Layout"} = sub {
243 clear_font_cache;
244 };
245
246 package CFClient::Item;
247
248 use strict;
249 use Crossfire::Protocol::Constants;
250
251 my $last_enter_count = 1;
252
253 sub desc_string {
254 my ($self) = @_;
255
256 my $desc =
257 $self->{nrof} < 2
258 ? $self->{name}
259 : "$self->{nrof} × $self->{name_pl}";
260
261 $self->{flags} & F_OPEN
262 and $desc .= " (open)";
263 $self->{flags} & F_APPLIED
264 and $desc .= " (applied)";
265 $self->{flags} & F_UNPAID
266 and $desc .= " (unpaid)";
267 $self->{flags} & F_MAGIC
268 and $desc .= " (magic)";
269 $self->{flags} & F_CURSED
270 and $desc .= " (cursed)";
271 $self->{flags} & F_DAMNED
272 and $desc .= " (damned)";
273 $self->{flags} & F_LOCKED
274 and $desc .= " *";
275
276 $desc
277 }
278
279 sub weight_string {
280 my ($self) = @_;
281
282 my $weight = ($self->{nrof} || 1) * $self->{weight};
283
284 $weight < 0 ? "?" : $weight * 0.001
285 }
286
287 sub do_n_dialog {
288 my ($cb) = @_;
289
290 my $w = new CFClient::UI::FancyFrame;
291 $w->add (my $vb = new CFClient::UI::VBox x => "center", y => "center");
292 $vb->add (new CFClient::UI::Label text => "Enter item count:");
293 $vb->add (my $entry = new CFClient::UI::Entry
294 text => $last_enter_count,
295 on_activate => sub {
296 my ($entry) = @_;
297 $last_enter_count = $entry->get_text;
298 $cb->($last_enter_count);
299 $w->hide;
300 $w = undef;
301 }
302 );
303 $entry->grab_focus;
304 $w->show;
305
306 }
307
308 sub update_widgets {
309 my ($self) = @_;
310
311 # necessary to avoid cyclic references
312 Scalar::Util::weaken $self;
313
314 my $button_cb = sub {
315 my (undef, $ev, $x, $y) = @_;
316
317 my $targ = $::CONN->{player}{tag};
318
319 if ($self->{container} == $::CONN->{player}{tag}) {
320 $targ = $::CONN->{open_container};
321 }
322
323 if (($ev->{mod} & CFClient::KMOD_SHIFT) && $ev->{button} == 1) {
324 $::CONN->send ("move $targ $self->{tag} 0")
325 if $targ || !($self->{flags} & F_LOCKED);
326 } elsif (($ev->{mod} & CFClient::KMOD_SHIFT) && $ev->{button} == 2) {
327 $self->{flags} & F_LOCKED
328 ? $::CONN->send ("lock " . pack "CN", 0, $self->{tag})
329 : $::CONN->send ("lock " . pack "CN", 1, $self->{tag})
330 } elsif ($ev->{button} == 1) {
331 $::CONN->send ("examine $self->{tag}");
332 } elsif ($ev->{button} == 2) {
333 $::CONN->send ("apply $self->{tag}");
334 } elsif ($ev->{button} == 3) {
335 my @menu_items = (
336 ["examine", sub { $::CONN->send ("examine $self->{tag}") }],
337 ["mark", sub { $::CONN->send ("mark ". pack "N", $self->{tag}) }],
338 ["apply", sub { $::CONN->send ("apply $self->{tag}") }],
339 (
340 $self->{flags} & F_LOCKED
341 ? (
342 ["unlock", sub { $::CONN->send ("lock " . pack "CN", 0, $self->{tag}) }],
343 )
344 : (
345 ["lock", sub { $::CONN->send ("lock " . pack "CN", 1, $self->{tag}) }],
346 ["drop", sub { $::CONN->send ("move $::CONN->{open_container} $self->{tag} 0") }],
347 ["move n",
348 sub {
349 do_n_dialog (sub { $::CONN->send ("move $targ $self->{tag} $_[0]") })
350 }
351 ]
352 )
353 ),
354 );
355
356 CFClient::UI::Menu->new (items => \@menu_items)->popup ($ev);
357 }
358
359 1
360 };
361
362 my $tooltip_std = "<small>"
363 . "Left click - examine item\n"
364 . "Shift-Left click - " . ($self->{container} ? "move or drop" : "take") . " item\n"
365 . "Middle click - apply\n"
366 . "Shift-Middle click - lock/unlock\n"
367 . "Right click - further options"
368 . "</small>\n";
369
370 $self->{face_widget} ||= new CFClient::UI::Face
371 can_events => 1,
372 can_hover => 1,
373 anim => $self->{anim},
374 animspeed => $self->{animspeed}, # TODO# must be set at creation time
375 on_button_down => $button_cb,
376 ;
377 $self->{face_widget}{face} = $self->{face};
378 $self->{face_widget}{anim} = $self->{anim};
379 $self->{face_widget}{animspeed} = $self->{animspeed};
380 $self->{face_widget}->set_tooltip (
381 "<b>Face/Animation.</b>\n"
382 . "Item uses face #$self->{face}. "
383 . ($self->{animspeed} ? "Item uses animation #$self->{anim} at " . (1 / $self->{animspeed}) . "fps. " : "Item is not animated. ")
384 . "\n\n$tooltip_std"
385 );
386
387 $self->{desc_widget} ||= new CFClient::UI::Label
388 can_events => 1,
389 can_hover => 1,
390 ellipsise => 2,
391 align => -1,
392 on_button_down => $button_cb,
393 ;
394 my $desc = CFClient::Item::desc_string $self;
395 $self->{desc_widget}->set_text ($desc);
396 $self->{desc_widget}->set_tooltip ("<b>$desc</b>.\n$tooltip_std");
397
398 $self->{weight_widget} ||= new CFClient::UI::Label
399 can_events => 1,
400 can_hover => 1,
401 ellipsise => 0,
402 align => 0,
403 on_button_down => $button_cb,
404 ;
405 $self->{weight_widget}->set_text (CFClient::Item::weight_string $self);
406
407 $self->{weight_widget}->set_tooltip (
408 "<b>Weight</b>.\n"
409 . ($self->{weight} >= 0 ? "One item weighs $self->{weight}g. " : "You have no idea how much this weighs. ")
410 . ($self->{nrof} ? "You have $self->{nrof} of it. " : "Item cannot stack with others of it's kind. ")
411 . "\n\n$tooltip_std"
412 );
413 }
414
415 1;
416
417 =back
418
419 =head1 AUTHOR
420
421 Marc Lehmann <schmorp@schmorp.de>
422 http://home.schmorp.de/
423
424 =cut
425