Changed the GDI driver interface to pass an opaque PHYSDEV pointer
[wine] / programs / winetest / include / wine.pm
1 # --------------------------------------------------------------------
2 # Module:      wine.pm
3 #
4 # Purpose: Module to supply wrapper around and support for gateway to
5 #          Windows API functions
6 #
7 # Copyright 2001 John F Sturtz for Codeweavers
8 #
9 # This library is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public
11 # License as published by the Free Software Foundation; either
12 # version 2.1 of the License, or (at your option) any later version.
13 #
14 # This library is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 # Lesser General Public License for more details.
18 #
19 # You should have received a copy of the GNU Lesser General Public
20 # License along with this library; if not, write to the Free Software
21 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 # --------------------------------------------------------------------
23
24 package wine;
25
26 use strict;
27 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $AUTOLOAD $todo_level
28             $successes $failures $todo_successes $todo_failures
29             %return_types %prototypes %loaded_modules);
30
31 require Exporter;
32
33 @ISA = qw(Exporter);
34
35 # Items to export into callers namespace by default. Note: do not export
36 # names by default without a very good reason. Use EXPORT_OK instead.
37 # Do not simply export all your public functions/methods/constants.
38 @EXPORT = qw(
39              AUTOLOAD
40              alloc_callback
41              assert
42              hd
43              ok
44              todo
45              todo_wine
46              trace
47              wc
48              wclen
49             );
50
51 $VERSION = '0.01';
52 bootstrap wine $VERSION;
53
54 # Global variables
55 $wine::err = 0;
56 $wine::exit_status = 0;
57 $wine::debug = defined($ENV{WINETEST_DEBUG}) ? $ENV{WINETEST_DEBUG} : 1;
58 $wine::platform = defined($ENV{WINETEST_PLATFORM}) ? $ENV{WINETEST_PLATFORM} : "windows";
59
60 $todo_level = 0;
61 $successes = 0;
62 $failures = 0;
63 $todo_successes = 0;
64 $todo_failures = 0;
65 %loaded_modules = ();
66
67 # --------------------------------------------------------------
68 # | Return-type constants                                      |
69 # |                                                            |
70 # | [todo]  I think there's a way to define these in a C       |
71 # |         header file, so that both the C functions in the   |
72 # |         XS module and the Perl routines in the .pm have    |
73 # |         access to them.  But I haven't worked it out       |
74 # |         yet ...                                            |
75 # --------------------------------------------------------------
76 %return_types = ( 
77     "void" => 0,
78     "int" => 1, "long" => 1,
79     "word" => 2,
80     "ptr" => 3,
81     "str" => 4, "wstr" => 4
82 );
83
84
85 # ------------------------------------------------------------------------
86 # | Sub:       AUTOLOAD                                                  |
87 # | -------------------------------------------------------------------- |
88 # | Purpose:   Used to catch calls to undefined routines                 |
89 # |                                                                      |
90 # |     Any routine which is called and not defined is assumed to be     |
91 # |     a call to the Wine API function of the same name.  We trans-     |
92 # |     late it into a call to the call() subroutine, with FUNCTION      |
93 # |     set to the function invoked and all other args passed thru.      |
94 # ------------------------------------------------------------------------
95 sub AUTOLOAD
96 {
97     # --------------------------------------------------------------
98     # | Figure out who we are                                      |
99     # --------------------------------------------------------------
100     my ($pkg, $func) = (split /::/, $AUTOLOAD)[0,1];
101
102     # --------------------------------------------------------------
103     # | Any function that is in the @EXPORT array is passed thru   |
104     # | to AutoLoader to pick up the appropriate XS extension      |
105     # --------------------------------------------------------------
106     if (grep ($_ eq $func, @EXPORT))
107     {
108         $AutoLoader::AUTOLOAD = $AUTOLOAD;
109         goto &AutoLoader::AUTOLOAD;
110     }
111
112     # --------------------------------------------------------------
113     # | Ignore this                                                |
114     # --------------------------------------------------------------
115     return
116         if ($func eq 'DESTROY');
117
118     # --------------------------------------------------------------
119     # | Otherwise, assume any undefined method is the name of a    |
120     # | wine API call, and all the args are to be passed through   |
121     # --------------------------------------------------------------
122     if (defined($prototypes{$func}))
123     {
124         return call( $func, @_ );
125     }
126     die "Function '$func' not declared";
127 } # End AUTOLOAD
128
129
130
131 # ------------------------------------------------------------------------
132 # | Sub:         call                                                    |
133 # | -------------------------------------------------------------------- |
134 # | Purpose:     Call a wine API function                                |
135 # |                                                                      |
136 # | Usage:       call FUNCTION, [ARGS ...]
137 # |                                                                      |
138 # | Returns:     value returned by API function called                   |
139 # ------------------------------------------------------------------------
140 sub call($@)
141 {
142     my ($function,@args) = @_;
143     my ($module,$funcptr,$ret_type,$arg_types) = @{$prototypes{$function}};
144
145     unless ($funcptr)
146     {
147         my $handle = $loaded_modules{$module};
148         $funcptr = get_proc_address( $handle, $function ) or die "Could not get address for $module.$function";
149         ${$prototypes{$function}}[1] = $funcptr;
150     }
151
152     if ($wine::debug > 1)
153     {
154         print STDERR "==== Call $function(";
155         for (@args)
156         {
157             print STDERR " ", +(ref () ? ("(" . ${$_} . ")") : "$_");
158         }
159         print STDERR " " if (scalar @args);
160         print STDERR ")\n";
161     }
162
163     # Check and translate args before call
164     my @args2;
165     if (defined($arg_types)) {
166         my @arg_types = @$arg_types;
167
168         if($#args != $#arg_types) {
169             die "$function: Wrong number of arguments, expected " .
170                 ($#arg_types + 1) . ", got " . ($#args + 1) . "\n";
171         }
172
173         while (defined(my $arg = shift @args) &&
174                defined(my $arg_type = shift @arg_types))
175         {
176             if($arg_type == 1 || $arg_type == 2) { # int || word
177                 $arg = int($arg);
178             }
179             push @args2, $arg;
180         }
181     } else {
182         @args2 = @args;
183     }
184
185     # Now call call_wine_API(), which will turn around and call
186     # the appropriate wine API function.
187     my ($err,$r) = call_wine_API( $funcptr, $ret_type, $wine::debug-1, @args2 );
188
189     if ($wine::debug > 1)
190     {
191         print STDERR "==== Ret  $function()";
192         if (defined($r)) { printf STDERR " ret=0x%x", $r; }
193         if (defined($err)) { printf STDERR " err=%d", $err; }
194         print STDERR "\n";
195     }
196
197     # Pass the return value back
198     $wine::err = $err;
199     return ($r);
200 }
201
202
203 # ----------------------------------------------------------------------
204 # | Subroutine:  declare
205 # ----------------------------------------------------------------------
206 sub declare($%)
207 {
208     my ($module, %list) = @_;
209     my ($handle, $func);
210
211     if (defined($loaded_modules{$module}))
212     {
213         $handle = $loaded_modules{$module};
214     }
215     else
216     {
217         $handle = load_library($module) or die "Could not load '$module'";
218         $loaded_modules{$module} = $handle;
219     }
220
221     foreach $func (keys %list)
222     {
223         if(ref($list{$func}) eq "ARRAY") {
224             my ($return_type, $argument_types) = @{$list{$func}};
225
226             my $ret_type = $return_types{$return_type};
227             my $arg_types = [map { $return_types{$_} } @$argument_types];
228
229             $prototypes{$func} = [ $module, 0, $ret_type, $arg_types ];
230         } else {
231             my $ret_type = $return_types{$list{$func}};
232
233             $prototypes{$func} = [ $module, 0, $ret_type ];
234         }
235     }
236 }
237
238
239 # ------------------------------------------------------------------------
240 # | Sub:         alloc_callback                                          |
241 # | -------------------------------------------------------------------- |
242 # | Purpose:     Allocate a thunk for a Wine API callback function.      |
243 # |                                                                      |
244 # |     Basically a thin wrapper over alloc_thunk(); see wine.xs for     |
245 # |     details ...                                                      |
246 # |                                                                      |
247 # | Usage:       alloc_callback SUB_REF, [ ARGS_TYPES ... ]              |
248 # |                                                                      |
249 # | Returns:     Pointer to thunk allocated (as an integer value)        |
250 # |                                                                      |
251 # |     The returned value is just a raw pointer to a block of memory    |
252 # |     allocated by the C code (cast into a Perl integer).  It isn't    |
253 # |     really suitable for anything but to be passed to a wine API      |
254 # |     function ...                                                     |
255 # ------------------------------------------------------------------------
256 sub alloc_callback($@)
257 {
258     # ----------------------------------------------
259     # | Locals                                     |
260     # |                                            |
261     # | [todo]  Check arg types                    |
262     # ----------------------------------------------
263     my  $sub_ref            = shift;
264     my  @callback_arg_types = @_;
265  
266     # [todo]  Check args
267     # [todo]  Some way of specifying args passed to callback
268
269     # --------------------------------------------------------------
270     # | Convert arg types to integers                              |
271     # --------------------------------------------------------------
272     map { $_ = $return_types{$_} } @callback_arg_types;
273
274     # --------------------------------------------------------------
275     # | Pass thru to alloc_thunk()                                 |
276     # --------------------------------------------------------------
277     return alloc_thunk ($sub_ref, @callback_arg_types);
278 }
279
280
281 # ----------------------------------------------------------------------
282 # | Subroutine:  hd                                                    |
283 # |                                                                    |
284 # | Purpose:     Display a hex dump of a string                        |
285 # |                                                                    |
286 # | Usage:       hd STR                                                |
287 # | Usage:       hd STR, LENGTH                                        |
288 # |                                                                    |
289 # | Returns:     (none)                                                |
290 # ----------------------------------------------------------------------
291 sub hd($;$)
292 {
293     # Locals
294     my  ($buf, $length);
295     my  $first;
296     my  ($str1, $str2, $str, $t);
297     my  ($c, $x);
298
299 # Begin sub hd
300
301     # --------------------------------------------------------------
302     # | Get args; if no BUF specified, blow                        |
303     # --------------------------------------------------------------
304     $buf = shift;
305     $length = (shift or length ($buf));
306     return
307         if ((not defined ($buf)) || ($length <= 0));
308
309     # --------------------------------------------------------------
310     # | Initialize                                                 |
311     # --------------------------------------------------------------
312     $first = 1;
313     $str1 = "00000:";
314     $str2 = "";
315
316     # --------------------------------------------------------------
317     # | For each character                                         |
318     # --------------------------------------------------------------
319     for (0 .. ($length - 1))
320     {
321         $c = substr ($buf, $_, 1);
322         $x = sprintf ("%02x", ord ($c));
323         $str1 .= (" " . $x);
324         $str2 .= (((ord ($c) >= 33) && (ord ($c) <= 126)) ? $c : ".");
325
326         # --------------------------------------------------------------
327         # | Every group of 4, add an extra space                       |
328         # --------------------------------------------------------------
329         if
330         (
331             ((($_ + 1) % 16) == 4)  ||
332             ((($_ + 1) % 16) == 12)
333         )
334         {
335             $str1 .= " ";
336             $str2 .= " ";
337         }
338
339         # --------------------------------------------------------------
340         # | Every group of 8, add a '-'                                |
341         # --------------------------------------------------------------
342         elsif
343         (
344             ((($_ + 1) % 16) == 8)
345         )
346         {
347             $str1 .= " -";
348             $str2 .= " ";
349         }
350
351         # --------------------------------------------------------------
352         # | Every group of 16, dump                                    |
353         # --------------------------------------------------------------
354         if
355         (
356             ((($_ + 1) % 16) == 0)      ||
357             ($_ == ($length - 1))
358         )
359         {
360             $str = sprintf ("%-64s%s", $str1, $str2);
361             if ($first)
362             {
363                 $t = ("-" x length ($str));
364                 print "  $t\n";
365                 print "  | $length bytes\n";
366                 print "  $t\n";
367                 $first = 0;
368             }
369             print "  $str\n";
370             $str1 = sprintf ("%05d:", ($_ + 1));
371             $str2 = "";
372             if ($_ == ($length - 1))
373             {
374                 print "  $t\n";
375             }
376         }
377
378     }  # end for
379
380
381     # --------------------------------------------------------------
382     # | Exit point                                                 |
383     # --------------------------------------------------------------
384     return;
385
386 } # End sub hd
387
388
389
390 # ----------------------------------------------------------------------
391 # | Subroutine:  wc                                                    |
392 # |                                                                    |
393 # | Purpose:     Generate unicode string                               |
394 # |                                                                    |
395 # | Usage:       wc ASCII_STRING                                       |
396 # |                                                                    |
397 # | Returns:     string generated                                      |
398 # ----------------------------------------------------------------------
399 sub wc($)
400 {
401     return pack("S*",unpack("C*",shift));
402 } # End sub wc
403
404
405
406 # ----------------------------------------------------------------------
407 # | Subroutine:  wclen                                                 |
408 # |                                                                    |
409 # | Purpose:     Return length of unicode string                       |
410 # |                                                                    |
411 # | Usage:       wclen UNICODE_STRING                                  |
412 # |                                                                    |
413 # | Returns:     string generated                                      |
414 # ----------------------------------------------------------------------
415 sub wclen($)
416 {
417     # Locals
418     my  $str = shift;
419     my  ($c1, $c2, $n);
420
421 # Begin sub wclen
422
423     $n = 0;
424     while (length ($str) > 0)
425     {
426         $c1 = substr ($str, 0, 1, "");
427         $c2 = substr ($str, 0, 1, "");
428         (($c1 eq "\x00") && ($c2 eq "\x00")) ? last : $n++;
429     }
430
431     return ($n);
432
433 } # End sub wclen
434
435
436
437 # ----------------------------------------------------------------------
438 # Subroutine:  ok
439 #
440 # Purpose:     Print warning if something fails
441 #
442 # Usage:       ok CONDITION [DESCRIPTION]
443 #
444 # Returns:     (none)
445 # ----------------------------------------------------------------------
446 sub ok($;$)
447 {
448     my $assertion = shift;
449     my $description = shift;
450     my ($filename, $line) = (caller (0))[1,2];
451     if ($todo_level)
452     {
453         if ($assertion)
454         {
455             print STDERR ("$filename:$line: Test succeeded inside todo block" .
456                           ($description ? ": $description" : "") . "\n");
457             $todo_failures++;
458         }
459         else { $todo_successes++; }
460     }
461     else
462     {
463         if (!$assertion)
464         {
465             print STDERR ("$filename:$line: Test failed" .
466                           ($description ? ": $description" : "") . "\n");
467             $failures++;
468         }
469         else { $successes++; }
470     }
471 }
472
473
474 # ----------------------------------------------------------------------
475 # Subroutine:  assert
476 #
477 # Purpose:     Print error and die if something fails
478 #
479 # Usage:       assert CONDITION [DESCRIPTION]
480 #
481 # Returns:     (none)
482 # ----------------------------------------------------------------------
483 sub assert($;$)
484 {
485     my $assertion = shift;
486     my $description = shift;
487     my ($filename, $line) = (caller (0))[1,2];
488     unless ($assertion)
489     {
490         die ("$filename:$line: Assertion failed" . ($description ? ": $description" : "") . "\n");
491     }
492 }
493
494
495 # ----------------------------------------------------------------------
496 # Subroutine:  trace
497 #
498 # Purpose:     Print debugging traces
499 #
500 # Usage:       trace format [arguments]
501 # ----------------------------------------------------------------------
502 sub trace($@)
503 {
504     return unless ($wine::debug > 0);
505     my $format = shift;
506     my $filename = (caller(0))[1];
507     $filename =~ s!.*/!!;
508     printf "trace:$filename $format", @_;
509 }
510
511 # ----------------------------------------------------------------------
512 # Subroutine:  todo
513 #
514 # Purpose:     Specify a block of code as todo for a given platform
515 #
516 # Usage:       todo name coderef
517 # ----------------------------------------------------------------------
518 sub todo($$)
519 {
520     my ($platform,$code) = @_;
521     if ($wine::platform eq $platform)
522     {
523         $todo_level++;
524         eval &$code;
525         $todo_level--;
526     }
527     else
528     {
529         eval &$code;
530     }
531 }
532
533
534 # ----------------------------------------------------------------------
535 # Subroutine:  todo_wine
536 #
537 # Purpose:     Specify a block of test as todo for the Wine platform
538 #
539 # Usage:       todo_wine { code }
540 # ----------------------------------------------------------------------
541 sub todo_wine(&)
542 {
543     my $code = shift;
544     todo( "wine", $code );
545 }
546
547
548 # ----------------------------------------------------------------------
549 # Subroutine:  END
550 #
551 # Purpose:     Called at the end of execution, print results summary
552 # ----------------------------------------------------------------------
553 END
554 {
555     return if $?;  # got some other error already
556     if ($wine::debug > 0)
557     {
558         my $filename = (caller(0))[1];
559         printf STDERR ("%s: %d tests executed, %d marked as todo, %d %s.\n",
560                        $filename, $successes + $failures + $todo_successes + $todo_failures,
561                        $todo_successes, $failures + $todo_failures,
562                        ($failures + $todo_failures != 1) ? "failures" : "failure" );
563     }
564     $? = ($failures + $todo_failures < 255) ? $failures + $todo_failures : 255;
565 }
566
567
568 # Autoload methods go after =cut, and are processed by the autosplit program.
569 1;
570 __END__
571
572
573
574 # ------------------------------------------------------------------------
575 # | pod documentation                                                    |
576 # |                                                                      |
577 # |                                                                      |
578 # ------------------------------------------------------------------------
579
580 =head1 NAME
581
582 wine - Perl extension for calling wine API functions
583
584 =head1 SYNOPSIS
585
586     use wine;
587
588     wine::declare( "kernel32",
589                    SetLastError => ["void", ["int"]],
590                    GetLastError => ["int", []] );
591     SetLastError( 1234 );
592     printf "%d\n", GetLastError();
593
594
595 =head1 DESCRIPTION
596
597 This module provides a gateway for calling Win32 API functions from
598 a Perl script.
599
600 =head1 CALLING WIN32 API FUNCTIONS
601
602 The functions you want to call must first be declared by calling
603 the wine::declare method. The first argument is the name of the
604 module containing the APIs, and the next argument is a list of
605 function names and their return and argument types. For instance:
606
607     wine::declare( "kernel32",
608                    SetLastError => ["void", ["int"]],
609                    GetLastError => ["int", []] );
610
611 declares that the functions SetLastError and GetLastError are
612 contained in the kernel32 dll.
613
614 Once you have done that you can call the functions directly just
615 like native Perl functions:
616
617   SetLastError( $some_error );
618
619 The supported return types are:
620
621 =over 4
622
623 =item void
624
625 =item word
626
627 =item long
628
629 =item ptr
630
631 =item str
632
633 =item wstr
634
635 =back
636
637 =head1 $wine::err VARIABLE
638
639 In the Win32 API, an integer error code is maintained which always
640 contains the status of the last API function called.  In C code,
641 it is accessed via the GetLastError() function.  From a Perl script,
642 it can be accessed via the package global $wine::err.  For example:
643
644     GlobalGetAtomNameA ($atom, \$buf, -1);
645     if ($wine::err == 234)
646     {
647         ...
648     }
649
650 Wine returns 234 (ERROR_MORE_DATA) from the GlobalGetAtomNameA()
651 API function in this case because the buffer length passed is -1
652 (hardly enough room to store anything in ...)
653
654 If the called API didn't set the last error code, $wine:;err is
655 undefined.
656
657 =head1 $wine::debug VARIABLE
658
659 This variable can be set to 1 to enable debugging of the API calls,
660 which will print a lot of information about what's going on inside the
661 wine package while calling an API function.
662
663 =head1 OTHER USEFUL FUNCTIONS
664
665 The bundle that includes the wine extension also includes a module of
666 plain ol' Perl subroutines which are useful for interacting with wine
667 API functions. Currently supported functions are:
668
669 =over 4
670
671 =item hd BUF [, LENGTH]
672
673 Dump a formatted hex dump to STDOUT.  BUF is a string containing
674 the buffer to dump; LENGTH is the length to dump (length (BUF) if
675 omitted).  This is handy because wine often writes a null character
676 into the middle of a buffer, thinking that the next piece of code to
677 look at the buffer will be a piece of C code that will regard it as
678 a string terminator.  Little does it know that the buffer is going
679 to be returned to a Perl script, which may not ...
680
681 =item wc STR
682
683 Generate and return a wide-character (Unicode) string from the given
684 ASCII string
685
686 =item wclen WSTR
687
688 Return the length of the given wide-character string
689
690 =item assert CONDITION
691
692 Print a message if the assertion fails (i.e., CONDITION is false),
693 or do nothing quietly if it is true.  The message includes the script
694 name and line number of the assertion that failed.
695
696 =back
697
698
699
700 =head1 AUTHOR
701
702 John F Sturtz, jsturtz@codeweavers.com
703
704 =head1 SEE ALSO
705
706 wine documentation
707
708 =cut