背景

DOS下的软件模拟图形界面的3D按钮是怎么实现的呢? 我们今天主要是利用VESA相关接口来实现DOS的3D按钮功能

VESA代码

types.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#ifndef __TYPES_H_INCLUDED
#define __TYPES_H_INCLUDED

#define ushort  unsigned short
#define ulong   unsigned long
#define uchar   unsigned char
#define uint    unsigned int
#define dword   signed long
#define word    signed short
#define byte    signed char
#define bool    signed int 

#endif

VESA.h

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#ifndef __VESA20_H_INCLUDED
#define __VESA20_H_INCLUDED

struct VBEInfo {
    char            vbe_signature[4];     // 'VESA' 4 byte signature
    short           vbe_version;          // VBE version number
    char           *oem_string_ptr;       // Pointer to OEM string
    unsigned long   capabilities;         // Capabilities of video card
    unsigned short *video_mode_ptr;       // Pointer to supported modes
    short           total_memory;         // Number of 64kb memory blocks
    // added for VBE 2.0
    short           oem_software_rev;     // OEM Software revision number
    char           *oem_vendor_name_ptr;  // Pointer to Vendor Name string
    char           *oem_product_name_ptr; // Pointer to Product Name string
    char           *oem_product_rev_ptr;  // Pointer to Product Revision str
    char            reserved[222];        // Pad to 256 byte block size
    char            oem_data[256];        // Scratch pad for OEM data

} __attribute__ ((packed));

typedef struct VBEInfo VBEINFO;

struct VBEModeInfo {
    // Mandatory information for all VBE revisions:
    unsigned short  mode_attributes;      // mode attributes
    unsigned char   win_a_attributes;     // window A attributes
    unsigned char   win_b_attributes;     // window B attributes
    unsigned short  win_granularity;      // window granularity
    unsigned short  win_size;             // window size
    unsigned short  win_a_segment;        // window A start segment
    unsigned short  win_b_segment;        // window B start segment
    unsigned long   win_func_ptr;         // pointer to window function
    unsigned short  bytes_per_scan_line;  // bytes per scan line

    // Mandatory information for VBE 1.2 and above:
    unsigned short  x_resolution;          // horizontal resolution in pixels or chars
    unsigned short  y_resolution;          // vertical resolution in pixels or chars
    unsigned char   x_char_size;           // character cell width in pixels
    unsigned char   y_char_size;           // character cell height in pixels
    unsigned char   number_of_planes;      // number of memory planes
    unsigned char   bits_per_pixel;        // bits per pixel
    unsigned char   number_of_banks;       // number of banks
    unsigned char   memory_model;          // memory model type
    unsigned char   bank_size;             // bank size in KB
    unsigned char   number_of_image_pages; // number of images
    unsigned char   reserved;              // reserved for page function

    // Direct Color fields (required for direct/6 and YUV/7 memory models)
    unsigned char   red_mask_size;          // size of direct color red mask in bits
    unsigned char   red_field_position;     // bit position of lsb of red mask
    unsigned char   green_mask_size;        // size of direct color green mask in bits
    unsigned char   green_field_position;   // bit position of lsb of green mask
    unsigned char   blue_mask_size;         // size of direct color blue mask in bits
    unsigned char   blue_field_position;    // bit position of lsb of blue mask
    unsigned char   rsvd_mask_size;         // size of direct color reserved mask in bits
    unsigned char   rsvd_field_position;    // bit position of lsb of reserved mask
    unsigned char   direct_color_mode_info; // direct color mode attributes

    // Mandatory information for VBE 2.0 and above:
    unsigned long   phys_base_ptr;          // physical address for flat frame buffer
    unsigned long   off_screen_mem_offset;  // pointer to start of off screen memory
    unsigned short  off_screen_mem_size;    // amount of off screen memory in 1k units
    char            reserved_buf[206];

} __attribute__ ((packed));

typedef struct VBEModeInfo MODEINFO;

struct PModeInterface {
    short set_window;
    short set_display_start;
    short set_palette;
    char  io_info;
} __attribute__ ((packed));

typedef struct PModeInterface PMINTERFACE;

struct VBESurface {
    unsigned long   lfb_linear_address;
    unsigned short  lfb_selector;
    void           *lfb_near_ptr;
    unsigned long   lfb_mem_size;
    unsigned short  x_resolution;
    unsigned short  y_resolution;
    unsigned long   bits_per_pixel;
    unsigned short  virtual_x_resolution;
    unsigned long   bytes_per_pixel;
    unsigned long   screen_bytes;
    unsigned long   screen_dwords;
    unsigned long   center_x;
    unsigned long   center_y;
    unsigned short  number_of_offscreens;
    void*           offscreen_ptr;
    unsigned char   vbe_boolean;
    unsigned char   vbe_init_boolean;
    signed   long   io_segment;
    unsigned long   io_linear;

};

typedef struct VBESurface VBESURFACE;

//********************************Functions*********************************//

VBEINFO    *VBEgetInfo(void);
MODEINFO   *VBEgetModeInfo(ushort vbe_mode_nr);
void       *VBEgetPmodeInterface(void);
ushort      VBEselectModeNr(ushort xres, ushort yres, uchar bpp);
VBESURFACE *VBEsetMode(ushort vbe_mode_nr);
VBESURFACE *VBEmodeInit(ushort xres, ushort yres, uchar bpp);
ushort      VBEsetScanlineLength(ushort pixel_length);
void        VBEsetDisplayStart(ulong pixel, ulong scanline);
VBESURFACE *VBEgetVbeSurfacePtr(void);
MODEINFO   *VBEgetModeInfoPtr(void);
VBESURFACE *VBEinfoInit(ushort xres, ushort yres, uchar bpp, ushort delay);
VBESURFACE *VBEinit(ushort xres, ushort yres, uchar bpp);
int         VBEshutdown(void);

void VGAcreatePalette(void);
void VGAwaitVrt(void);

inline void putPixel8(long x, long y, ulong color);
inline void putPixel16(long x, long y, ulong color);
inline void putPixel32(long x, long y, ulong color);
inline void flipScreen(void);

extern void (*putPixel)(long x, long y, ulong color);

#endif

VESA.C

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
/*****************************************************************************
*               VBE 2.0 detection and routines for DJGPP C.                  *
*****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dpmi.h>
#include <go32.h>
#include <conio.h>
#include <dos.h>
#include <sys/nearptr.h>
#include <sys/farptr.h>
#include "types.h"
#include "vesa.h"

#define global

static VBESURFACE   _vbe_surface;
static VBEINFO      _vbe_info;
static MODEINFO     _vbe_mode_info;
static PMINTERFACE *_pmode_interface_ptr;
static ulong       *_ytable;

static void (*PM_VBEsetDisplayStart)(void);

global void (*putPixel)(long x, long y, ulong color);

/*****************************************************************************
*                                                                            *
*    VBEgetInfo returns a pointer to a VBEINFO structure or NULL on error    *
*                                                                            *
*****************************************************************************/
VBEINFO *VBEgetInfo(void)
{
    ushort counter = 0;
    ushort offset  = 0;
    ushort vbe_mode = 0xFFFF;
    ushort *vbe_mode_list = 0;
    __dpmi_regs r;

    // we want VBE 2.0+ info
    strncpy(_vbe_info.vbe_signature, "VBE2", 4);

    // request info
    r.x.ax = 0x4F00;
    r.x.di = __tb & 0x0F;
    r.x.es = (__tb >> 4) & 0xFFFF;
    dosmemput(&_vbe_info, sizeof(VBEINFO), __tb);
    __dpmi_int(0x10, &r);
    if(r.x.ax != 0x004F) return(0);
    dosmemget(__tb, sizeof(VBEINFO), &_vbe_info);

    if(strncmp(_vbe_info.vbe_signature, "VESA", 4) != 0) return(0);  // VESA ?

    // get the videomode list
    do {
        dosmemget(((((long)_vbe_info.video_mode_ptr >> 16) & 0xFFFF) << 4) +
                  ((long)_vbe_info.video_mode_ptr & 0xFFFF) +
                  (counter * sizeof(short)),
                  sizeof(short), &vbe_mode);
        counter++;
    } while(vbe_mode != 0xFFFF);
    vbe_mode_list = malloc((counter + 1) * sizeof(short));
    counter = 0;

    do {
        dosmemget(((((long)_vbe_info.video_mode_ptr >> 16) & 0xFFFF) << 4) +
                  ((long)_vbe_info.video_mode_ptr & 0xFFFF) +
                  (counter * sizeof(short)),
                  sizeof(short), &vbe_mode_list[counter]);
        counter++;
    } while(vbe_mode_list[counter-1] != 0xFFFF);
    _vbe_info.video_mode_ptr = vbe_mode_list;
    counter = 0;

    // get the OEM string
    do {
        dosmemget(((((long)_vbe_info.oem_string_ptr >> 16) & 0xFFFF) << 4) +
                  ((long)_vbe_info.oem_string_ptr & 0xFFFF) + counter,
                  sizeof(char), &_vbe_info.oem_data[counter]);
        counter++;
    } while(_vbe_info.oem_data[counter-1] != 0);
    _vbe_info.oem_string_ptr = &_vbe_info.oem_data[0];
    offset  = counter;
    counter = 0;

    if(_vbe_info.vbe_version >= 0x0200) {

        // get the vendor name
        do {
            dosmemget(((((long)_vbe_info.oem_vendor_name_ptr >> 16) & 0xFFFF) << 4) +
                      ((long)_vbe_info.oem_vendor_name_ptr & 0xFFFF) + counter,
                      sizeof(char), &_vbe_info.oem_data[counter+offset]);
            counter++;
        } while(_vbe_info.oem_data[counter+offset-1] != 0);
        _vbe_info.oem_vendor_name_ptr = &_vbe_info.oem_data[offset];
        offset  = offset + counter;
        counter = 0;

        // get the product name
        do {
            dosmemget(((((long)_vbe_info.oem_product_name_ptr >> 16) & 0xFFFF) << 4) +
                      ((long)_vbe_info.oem_product_name_ptr & 0xFFFF) + counter,
                      sizeof(char), &_vbe_info.oem_data[counter+offset]);
            counter++;
        } while(_vbe_info.oem_data[counter+offset-1] != 0);
        _vbe_info.oem_product_name_ptr = &_vbe_info.oem_data[offset];
        offset  = offset + counter;
        counter = 0;

        //get the product revision
        do {
            dosmemget(((((long)_vbe_info.oem_product_rev_ptr >> 16) & 0xFFFF) << 4) +
                      ((long)_vbe_info.oem_product_rev_ptr & 0xFFFF) + counter,
                      sizeof(char), &_vbe_info.oem_data[counter+offset]);
            counter++;
        } while(_vbe_info.oem_data[counter+offset-1] != 0);
        _vbe_info.oem_product_rev_ptr = &_vbe_info.oem_data[offset];
    }

    _vbe_surface.vbe_boolean = 1;
    return(&_vbe_info);
}

/*****************************************************************************
*                                                                            *
* VBEgetModeInfo takes a ModeNr on entry and returns a pointer to a          *
* ModeInfoBlock Structure or NULL on failure.                                *
*                                                                            *
*****************************************************************************/
MODEINFO *VBEgetModeInfo(unsigned short vbe_mode_nr)
{
    __dpmi_regs r;

    if(_vbe_surface.vbe_boolean == 0) return(0);

    r.x.ax = 0x4F01;
    r.x.cx = vbe_mode_nr;
    r.x.di = __tb & 0x0F;
    r.x.es = (__tb >> 4) & 0xFFFF;
    __dpmi_int(0x10, &r);

    if(r.x.ax != 0x004F) return(0);

    dosmemget(__tb, sizeof(MODEINFO), &_vbe_mode_info);

    if(_vbe_mode_info.bits_per_pixel == 16) // for buggy VBE implementations
        _vbe_mode_info.bits_per_pixel = _vbe_mode_info.red_mask_size +
                                        _vbe_mode_info.green_mask_size +
                                        _vbe_mode_info.blue_mask_size;
    return(&_vbe_mode_info);
}

/*****************************************************************************
*                                                                            *
*                      Get protected mode interface                          *
*                                                                            *
*****************************************************************************/
void *VBEgetPmodeInterface(void)
{
    __dpmi_regs r;
    __dpmi_meminfo m;
    ushort *ptr;

    // only available in VBE 2.0+
    if(_vbe_info.vbe_version < 0x200)  return(0);

    r.x.ax = 0x4F0A;
    r.x.bx = 0;
    __dpmi_int(0x10, &r);
    if(r.x.ax != 0x004F) return(0);

    // copy interface
    _pmode_interface_ptr = malloc(r.x.cx);
    dosmemget((r.x.es * 16) + r.x.di, r.x.cx, _pmode_interface_ptr);
    _go32_dpmi_lock_data(_pmode_interface_ptr, r.x.cx);

    // need memory-mapped IO?
    if(_pmode_interface_ptr->io_info) {
        ptr = (ushort *)((char *)_pmode_interface_ptr + _pmode_interface_ptr->io_info);
        while(*ptr != 0xFFFF) ptr++;
        ptr++;                                       // skip the port table and
        if(*ptr != 0xFFFF) {                         // get descriptor
            m.address = *((ulong *)ptr);
            m.size = *(ptr + 2);
            if(__dpmi_physical_address_mapping(&m) != 0) return(0);

            _vbe_surface.io_linear = m.address;
            __dpmi_lock_linear_region(&m);

            _vbe_surface.io_segment = __dpmi_allocate_ldt_descriptors(1);
            if(_vbe_surface.io_segment < 0) {
                __dpmi_unlock_linear_region(&m);
                __dpmi_free_physical_address_mapping(&m);
                return(0);
            }

            __dpmi_set_segment_base_address(_vbe_surface.io_segment,
                                            _vbe_surface.io_linear);
            __dpmi_set_segment_limit(_vbe_surface.io_segment, m.size - 1);

        }
    }

    PM_VBEsetDisplayStart = (void *)((char *)_pmode_interface_ptr +
                                     _pmode_interface_ptr->set_display_start);
    return(PM_VBEsetDisplayStart);
}

/*****************************************************************************
*                                                                            *
* VBEselectModeNr searches the vbe_mode_list for a Mode that matches the       *
* requirements given on entry (xres, yres, bpp).                             *
*                                                                            *
*****************************************************************************/
ushort VBEselectModeNr(ushort xres, ushort yres, uchar bpp)
{
    ushort counter;

    if(_vbe_surface.vbe_boolean == 0) return(0);

    for(counter = 0; ; counter++) {
        if(_vbe_info.video_mode_ptr[counter] == 0xFFFF) return(0);

        VBEgetModeInfo(_vbe_info.video_mode_ptr[counter]);

        if(_vbe_mode_info.x_resolution  == xres &&
                _vbe_mode_info.y_resolution  == yres &&
                _vbe_mode_info.bits_per_pixel == bpp) break;
    }

    return(_vbe_info.video_mode_ptr[counter]);
}

/*****************************************************************************
*                                                                            *
* Set inquired Mode and return a VbeSurface Structure or Null on failure     *
*                                                                            *
*****************************************************************************/
VBESURFACE *VBEsetMode(ushort vbe_mode_nr)
{
    __dpmi_regs r;
    __dpmi_meminfo m;
    ulong counter;

    if(_vbe_surface.vbe_boolean == 0) return(0);
    if(_vbe_surface.vbe_init_boolean == 1) return(0);
    if(VBEgetModeInfo(vbe_mode_nr) == 0) return(0);

    // create lookup table for putPixel routines
    if(_ytable) free(_ytable);
    if((_ytable = malloc(4 * (_vbe_mode_info.y_resolution + 1))) == 0) return(0);
    for(counter = 0; counter <= _vbe_mode_info.y_resolution; counter++)
        _ytable[counter] = _vbe_mode_info.x_resolution * counter *
                           ((_vbe_mode_info.bits_per_pixel + 1) / 8);

    // request frame buffer
    r.x.ax = 0x4F02;
    r.x.bx = (vbe_mode_nr | 0x4000);
    __dpmi_int(0x10, &r);
    if(r.x.ax != 0x004F) return(0);

    m.size = (_vbe_info.total_memory * 64 * 1024);
    m.address = _vbe_mode_info.phys_base_ptr;
    __dpmi_physical_address_mapping(&m);
    __dpmi_lock_linear_region(&m);

    _vbe_surface.lfb_linear_address = m.address;
    _vbe_surface.lfb_selector = __dpmi_allocate_ldt_descriptors(1);

    __dpmi_set_segment_base_address(_vbe_surface.lfb_selector,
                                    _vbe_surface.lfb_linear_address);
    __dpmi_set_segment_limit(_vbe_surface.lfb_selector,
                             (_vbe_info.total_memory * 64 * 1024) - 1);

    _vbe_surface.lfb_near_ptr = (void*)(_vbe_surface.lfb_linear_address
                                        + __djgpp_conventional_base);
    _vbe_surface.x_resolution         = _vbe_mode_info.x_resolution;
    _vbe_surface.y_resolution         = _vbe_mode_info.y_resolution;
    _vbe_surface.bits_per_pixel       = _vbe_mode_info.bits_per_pixel;
    _vbe_surface.virtual_x_resolution = _vbe_mode_info.x_resolution;
    _vbe_surface.bytes_per_pixel      = (_vbe_mode_info.bits_per_pixel + 1) / 8;
    _vbe_surface.center_x             = _vbe_surface.virtual_x_resolution / 2;
    _vbe_surface.center_y             = _vbe_surface.y_resolution / 2;
    _vbe_surface.number_of_offscreens =
        VBEsetScanlineLength(_vbe_mode_info.x_resolution) / _vbe_mode_info.y_resolution;
    _vbe_surface.vbe_init_boolean     = 1;
    _vbe_surface.screen_bytes         = (_vbe_surface.x_resolution *
                                         _vbe_surface.y_resolution *
                                         _vbe_surface.bytes_per_pixel);
    _vbe_surface.screen_dwords        = _vbe_surface.screen_bytes / 4;
    _vbe_surface.lfb_mem_size         = (_vbe_surface.x_resolution *
                                         _vbe_surface.y_resolution *
                                         _vbe_surface.bytes_per_pixel) / 1024 +
                                        _vbe_mode_info.off_screen_mem_size;

    for(counter = 0; counter < (_vbe_surface.lfb_mem_size * 1024); counter++)
        _farpokeb(_vbe_surface.lfb_selector, counter, 0x0); // clear Lfb

    if(_vbe_surface.offscreen_ptr) free(_vbe_surface.offscreen_ptr);
    if((_vbe_surface.offscreen_ptr = malloc(_vbe_surface.screen_bytes)) == 0)
        return(0);
    else
        memset(_vbe_surface.offscreen_ptr, 0, _vbe_surface.screen_bytes);

    if(_vbe_surface.bits_per_pixel == 8) {
        putPixel = putPixel8;
        VGAcreatePalette();
    }
    if(_vbe_surface.bits_per_pixel == 16) putPixel = putPixel16;
    if(_vbe_surface.bits_per_pixel == 15) putPixel = putPixel16;
    if(_vbe_surface.bits_per_pixel == 32) putPixel = putPixel32;

    return(&_vbe_surface);
}

/*****************************************************************************
*                                                                            *
*           VBEmodeInit switches to the desired resolution                   *
*                                                                            *
*****************************************************************************/
VBESURFACE *VBEmodeInit(ushort xres, ushort yres, uchar bpp)
{
    ushort vbe_mode_nr;
    __dpmi_meminfo m;

    if(_vbe_surface.vbe_boolean == 0) return(0);
    if(_vbe_surface.vbe_init_boolean == 0) return(0);

    m.size = (_vbe_info.total_memory * 64 * 1024);
    m.address = _vbe_surface.lfb_linear_address;
    __dpmi_unlock_linear_region(&m);
    __dpmi_free_physical_address_mapping(&m);
    __dpmi_free_ldt_descriptor(_vbe_surface.lfb_selector);

    _vbe_surface.vbe_init_boolean = 0;

    if((vbe_mode_nr = VBEselectModeNr(xres, yres, bpp)) == NULL) return(0);

    vbe_mode_nr |= 0x8000;
    if(VBEsetMode(vbe_mode_nr) == NULL) return(0);

    return(&_vbe_surface);
}

/*****************************************************************************
*                                                                            *
*       VBEsetScanlineLength returns the MaxNum of logical scanlines         *
*                                                                            *
*****************************************************************************/
ushort VBEsetScanlineLength(ushort pixel_length)
{
    __dpmi_regs r;

    if(_vbe_surface.vbe_boolean == 0) return(0);

    r.x.ax = 0x4F06;
    r.x.bx = 0x0000;
    r.x.cx = pixel_length;
    __dpmi_int(0x10, &r);
    if(r.h.ah != 0) return(0);
    if(r.x.cx != pixel_length) return(0);

    _vbe_surface.virtual_x_resolution = pixel_length;

    return(r.x.dx);
}

/*****************************************************************************
*                                                                            *
*                       VBEsetDisplayStart: let's scroll!                    *
*                                                                            *
*****************************************************************************/
void VBEsetDisplayStart(ulong pixel, ulong scanline)
{
    __dpmi_regs r;
    long address;
    long selector;

    if(PM_VBEsetDisplayStart) {
        address = (_ytable[scanline] + (pixel * _vbe_surface.bytes_per_pixel)) >> 2;

        selector = (_vbe_surface.io_segment) ? _vbe_surface.io_segment : _my_ds();

        asm(
            "  pushw %%es ; "
            "  movw %w1, %%es ; "          // set the IO segment
            "  call *%0 ; "                // call the VESA function
            "  popw %%es "
            :                              // no outputs

            : "S"(PM_VBEsetDisplayStart),  // function pointer in esi
            "a"(selector),                 // IO segment in eax
            "b"(0x0080),                   // mode in ebx
            "c"(address & 0xFFFF),         // low word of address in ecx
            "d"((address >> 16))           // high word of address in edx

            : "memory", "%edi", "%cc"      // touches edi and flags
        );

        return;
    }

    r.x.ax = 0x4F07;
    r.x.bx = 0x0080;
    r.x.cx = pixel;
    r.x.dx = scanline;
    __dpmi_int(0x10, &r);

}

/*****************************************************************************
*                                                                            *
*                         VBEgetVbeSurfacePtr(void)                          *
*                                                                            *
*****************************************************************************/
VBESURFACE *VBEgetVbeSurfacePtr(void)
{
    return(&_vbe_surface);
}

/*****************************************************************************
*                                                                            *
*                         VBEgetModeInfoPtr(void)                            *
*                                                                            *
*****************************************************************************/
MODEINFO *VBEgetModeInfoPtr(void)
{
    return(&_vbe_mode_info);
}

/*****************************************************************************
*                                                                            *
*   VBEinit silently enters the desired mode and returns VbeSurface          *
*                                                                            *
*****************************************************************************/
VBESURFACE *VBEinit(ushort xres, ushort yres, uchar bpp)
{
    ushort vbe_mode_nr;

    if(VBEgetInfo() == NULL) {
        puts("\nNo Vesa Bios Extension found...");
        return(0);
    }

    if(_vbe_info.vbe_version < 0x0200) {
        puts("\nVBE Version 2.0 or better required...");
        return(0);
    }

    VBEgetPmodeInterface();

    if((vbe_mode_nr = VBEselectModeNr(xres, yres, bpp)) == NULL) {
        puts("\nNo appropriate Videomode available...");
        return(0);
    }

    if(VBEsetMode(vbe_mode_nr) == NULL)
        return(0);
    else
        return(&_vbe_surface);
}

/*****************************************************************************
*                                                                            *
*   VBEinit shows an info screen then enters the mode, returns VbeSurface    *
*                                                                            *
*****************************************************************************/
VBESURFACE *VBEinfoInit(ushort xres, ushort yres, uchar bpp, ushort delay_ms)
{
    ushort vbe_mode_nr;
    char  *LFBmsg[] = { "NOLFB present", "LFB present"};
    signed char counter;

    puts("\nVBE2.0 driver v1.0 (c) 1999, Tobias Koch <tobias.koch@gmail.com>\n");

    if(VBEgetInfo() == NULL) {
        puts("\n\nNo VESA Bios Extension found...\n\n");
        return(0);
    }
    printf("Video Memory      - %d KB           \n"
           "VBE version       - %d.%d  detected \n"
           "OEM spec.         - %s              \n",
           _vbe_info.total_memory * 64,
           _vbe_info.vbe_version >> 8, _vbe_info.vbe_version & 0x00FF,
           _vbe_info.oem_string_ptr);

    if(_vbe_info.vbe_version >= 0x0200) {
        printf("OEM software rev. - %d.%d\n"
               "OEM vendor name   - %s   \n"
               "OEM product name  - %s   \n"
               "OEM product rev.  - %s   \n",
               _vbe_info.oem_software_rev >> 8, _vbe_info.oem_software_rev & 0x00FF,
               _vbe_info.oem_vendor_name_ptr,
               _vbe_info.oem_product_name_ptr,
               _vbe_info.oem_product_rev_ptr);
    } else puts("\nVESA Bios Extension 2.0 or better required! "
                    "\nAttempting to set the mode anyway...");

    if(VBEgetPmodeInterface())
        puts("\nProtected Mode Interface detected ... ");

    if((vbe_mode_nr = VBEselectModeNr(xres, yres, bpp)) == NULL) {
        puts("\nNo appropriate Videomode Available...");
        return(0);
    } else if(VBEgetModeInfo(vbe_mode_nr) != NULL) {
        printf("\nMode %Xh - %dx%dx%dbpp - RGB %d:%d:%d - %s\n\n",
               vbe_mode_nr,
               _vbe_mode_info.x_resolution,
               _vbe_mode_info.y_resolution,
               _vbe_mode_info.bits_per_pixel,
               _vbe_mode_info.red_mask_size,
               _vbe_mode_info.green_mask_size,
               _vbe_mode_info.blue_mask_size,
               LFBmsg[((_vbe_mode_info.mode_attributes) & (1<<7)) >> 7]);
    }

    delay_ms /= 10; // 10...9...8...7...6...5...4...3...2...1...GO!
    if(delay_ms > 0) {
        for(counter = 10; counter > 0; counter--) {
            delay(delay_ms);
            printf("%d..", counter);
        }
        delay(delay_ms);
        printf("Launch");
        delay(delay_ms);
    } else getch();

    if(VBEsetMode(vbe_mode_nr) == NULL) {
        VBEshutdown();
        puts("\n\nUnable to access linear frame buffered mode!"
             "\nTry loading UNIVBE by Scitech Software (tm).  ");
        return(0);
    }

    return(&_vbe_surface);
}

/*****************************************************************************
*                                                                            *
*                VBEshutdown cleans up descriptors, memory, etc.             *
*                                                                            *
*****************************************************************************/
int VBEshutdown(void)
{
    __dpmi_regs r;
    __dpmi_meminfo m;

    if(_vbe_surface.vbe_init_boolean == 0) {
        r.x.ax = 0x03;               // make sure we're in 3h
        __dpmi_int(0x10, &r);        // for buggy vesa implementations
        puts("\nuninitialized...");
        return(1);
    }

    // free mapping etc.
    m.size = (_vbe_info.total_memory * 64 * 1024);
    m.address = _vbe_surface.lfb_linear_address;
    __dpmi_unlock_linear_region(&m);
    __dpmi_free_physical_address_mapping(&m);
    __dpmi_free_ldt_descriptor(_vbe_surface.lfb_selector);

    // get rid of PMI interface
    if(_pmode_interface_ptr) free(_pmode_interface_ptr);
    if(_vbe_surface.io_segment) {
        m.address = _vbe_surface.io_linear;
        __dpmi_free_physical_address_mapping(&m);
        __dpmi_free_ldt_descriptor(_vbe_surface.io_segment);
    }

    if(_vbe_surface.offscreen_ptr) free(_vbe_surface.offscreen_ptr);

    // return do DOS
    r.x.ax = 0x03;
    __dpmi_int(0x10, &r);

    // deinit VBE surface
    _vbe_surface.vbe_init_boolean = 0;
    _vbe_surface.vbe_boolean = 0;

    puts("Elvis has left the building...");

    // dealloc mode list
    free(_vbe_info.video_mode_ptr);
    return(0);
}

/*****************************************************************************
*                                                                            *
*              Create a Palette for 3:3:2 True-Color emulation               *
*                                                                            *
*****************************************************************************/
void VGAcreatePalette(void)
{
    unsigned char color, red, green, blue;

    color = 0;
    for(red = 4; red < 68; red += 8) {
        for(green = 4; green < 68; green += 8) {
            for(blue = 4; blue < 68; blue += 16) {
                outportb(0x3C8, color);
                outportb(0x3C9, red);
                outportb(0x3C9, green);
                outportb(0x3C9, blue);
                color++;
            }
        }
    }
}

/*****************************************************************************
*                                                                            *
*                         Wait for Vertical Retrace                          *
*                                                                            *
*****************************************************************************/
void VGAwaitVrt(void)
{
    while(inportb(0x3DA) & 8);
    while(!(inportb(0x3DA) & 8));
}

/*****************************************************************************
*                                                                            *
*                  putPixel for 8Bit Screen modes                            *
*                                                                            *
*****************************************************************************/
inline void putPixel8(long x, long y, ulong color)
{
    x = +x + _vbe_surface.center_x;
    y = -y + _vbe_surface.center_y;

    _farpokeb(_vbe_surface.lfb_selector, _ytable[y] + x , color);
}

/*****************************************************************************
*                                                                            *
*                  putPixel for 16Bit Screen modes                           *
*                                                                            *
*****************************************************************************/
inline void putPixel16(long x, long y, ulong color)
{
    x = +x + _vbe_surface.center_x;
    y = -y + _vbe_surface.center_y;

    _farpokew(_vbe_surface.lfb_selector, _ytable[y] + (x << 1), color);
}

/*****************************************************************************
*                                                                            *
*                  putPixel for 32Bit Screen modes                           *
*                                                                            *
*****************************************************************************/
inline void putPixel32(long x, long y, ulong color)
{
    x = +x + _vbe_surface.center_x;
    y = -y + _vbe_surface.center_y;

    _farpokel(_vbe_surface.lfb_selector, _ytable[y] + (x << 2), color);
}

/*****************************************************************************
*                                                                            *
*                      Flip Offsreen to active Screen                        *
*                                                                            *
*****************************************************************************/
inline void flipScreen(void)
{
    _movedatal(_my_ds(), (long)_vbe_surface.offscreen_ptr,
               _vbe_surface.lfb_selector, 0x0, _vbe_surface.screen_dwords);
}

3D按钮代码

首先,我们可以创建一个名为 color.h 的头文件,用于定义颜色结构:

1
2
3
4
5
6
7
8
9
// color.h
#ifndef COLOR_H
#define COLOR_H

typedef struct {
    unsigned char red, green, blue;
} Color;

#endif // COLOR_H

接下来,我们创建 button.h,其中包含按钮和按钮状态的定义:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// button.h
#ifndef BUTTON_H
#define BUTTON_H

#include "color.h"

// 按钮状态枚举
typedef enum {
    BUTTON_NORMAL,
    BUTTON_PRESSED,
    BUTTON_RELEASED,
    BUTTON_SELECTED
} ButtonState;

// 按钮结构体
typedef struct {
    int x, y;                   // 按钮位置
    int width, height;          // 按钮大小
    Color color;                // 按钮颜色
    ButtonState state;          // 按钮状态
    char *label;                // 按钮标签
    char shortcutKey;           // 快捷键
} Button;

#endif // BUTTON_H

然后,为按钮组创建 button_group.h:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// button_group.h
#ifndef BUTTON_GROUP_H
#define BUTTON_GROUP_H

#include "button.h"

// 布局方向枚举
typedef enum {
    LAYOUT_HORIZONTAL,
    LAYOUT_VERTICAL
} LayoutDirection;

// 按钮组结构体
typedef struct {
    Button *buttons;            // 按钮数组
    int buttonCount;            // 按钮数量
    LayoutDirection layout;     // 布局方向
    int spacing;                // 按钮间距
} ButtonGroup;

#endif // BUTTON_GROUP_H

最后,创建一个包含绘制函数声明的头文件 drawing.h:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// drawing.h
#ifndef DRAWING_H
#define DRAWING_H

#include "button_group.h"

void draw3DButton(const Button *button);
void drawButtonGroup(const ButtonGroup *group);
Color getHighlightColor(Color baseColor);
Color getShadowColor(Color baseColor);

#endif // DRAWING_H
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 颜色结构
typedef struct {
    unsigned char red, green, blue;
} Color;

// 按钮状态枚举
typedef enum {
    BUTTON_NORMAL,
    BUTTON_PRESSED,
    BUTTON_RELEASED,
    BUTTON_SELECTED
} ButtonState;

// 按钮结构体
typedef struct {
    int x, y;                   // 按钮位置
    int width, height;          // 按钮大小
    Color color;                // 按钮颜色
    ButtonState state;          // 按钮状态
    char *label;                // 按钮标签
    char shortcutKey;           // 快捷键
} Button;

// 布局方向枚举
typedef enum {
    LAYOUT_HORIZONTAL,
    LAYOUT_VERTICAL
} LayoutDirection;

// 按钮组结构体
typedef struct {
    Button *buttons;            // 按钮数组
    int buttonCount;            // 按钮数量
    LayoutDirection layout;     // 布局方向
    int spacing;                // 按钮间距
} ButtonGroup;

// 函数原型声明
void draw3DButton(const Button *button);
void drawButtonGroup(const ButtonGroup *group);
Color getHighlightColor(Color baseColor);
Color getShadowColor(Color baseColor);

// 主函数
int main() {
    // 创建按钮
    Button buttons[3];
    buttons[0] = (Button){10, 10, 100, 30, {255, 0, 0}, BUTTON_NORMAL, "Button 1", 'A'};
    buttons[1] = (Button){10, 50, 100, 30, {0, 255, 0}, BUTTON_NORMAL, "Button 2", 'B'};
    buttons[2] = (Button){10, 90, 100, 30, {0, 0, 255}, BUTTON_NORMAL, "Button 3", 'C'};

    // 创建按钮组
    ButtonGroup group = {
        buttons,
        3,
        LAYOUT_VERTICAL,
        10
    };

    // 绘制按钮组
    drawButtonGroup(&group);

    return 0;
}

// 辅助函数:计算高光颜色
Color getHighlightColor(Color baseColor) {
    // 以下是一种简单的方法来计算高光颜色(可根据需要调整)
    Color highlight = { 
        baseColor.red < 205 ? baseColor.red + 50 : 255,
        baseColor.green < 205 ? baseColor.green + 50 : 255,
        baseColor.blue < 205 ? baseColor.blue + 50 : 255
    };
    return highlight;
}

// 辅助函数:计算阴影颜色
Color getShadowColor(Color baseColor) {
    // 以下是一种简单的方法来计算阴影颜色(可根据需要调整)
    Color shadow = {
        baseColor.red > 50 ? baseColor.red - 50 : 0,
        baseColor.green > 50 ? baseColor.green - 50 : 0,
        baseColor.blue > 50 ? baseColor.blue - 50 : 0
    };
    return shadow;
}

// 绘制3D按钮
void draw3DButton(const Button *button) {
    // 这里应该有实际的绘制代码
    printf("Drawing 3D button '%s' at (%d, %d)\n", button->label, button->x, button->y);
    
    // 模拟绘制按钮主体
    Color baseColor = button->color;
    Color highlightColor = getHighlightColor(baseColor);
    Color shadowColor = getShadowColor(baseColor);
    printf("  Base color: RGB(%d, %d, %d)\n", baseColor.red, baseColor.green, baseColor.blue);
    printf("  Highlight color: RGB(%d, %d, %d)\n", highlightColor.red, highlightColor.green, highlightColor.blue);
    printf("  Shadow color: RGB(%d, %d, %d)\n", shadowColor.red, shadowColor.green, shadowColor.blue);

    // 在这里添加实际的图形绘制代码
    // 例如,使用图形库绘制矩形、文本等
}

// 绘制按钮组
void drawButtonGroup(const ButtonGroup *group) {
    int offsetX = group->buttons[0].x;
    int offsetY = group->buttons[0].y;

    for (int i = 0; i < group->buttonCount; ++i) {
        // 设置按钮位置
        Button button = group->buttons[i];
        button.x = offsetX;
        button.y = offsetY;

        // 绘制按钮
        draw3DButton(&button);

        // 更新下一个按钮的位置
        if (group->layout == LAYOUT_HORIZONTAL) {
            offsetX += button.width + group->spacing;
        } else { // LAYOUT_VERTICAL
            offsetY += button.height + group->spacing;
        }
    }
}