// Filename: winGraphicsPipe.cxx
// Created by:  drose (20Dec02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#include "winGraphicsPipe.h"
#include "config_windisplay.h"
#include "displaySearchParameters.h"
#include "dtool_config.h"
#include "pbitops.h"

#include "psapi.h"
#include "powrprof.h"

#ifdef _WIN64
#include <intrin.h>
#endif


TypeHandle WinGraphicsPipe::_type_handle;

#define MAXIMUM_PROCESSORS 32

typedef struct _PROCESSOR_POWER_INFORMATION 
{  
  ULONG Number;  
  ULONG MaxMhz;  
  ULONG CurrentMhz;  
  ULONG MhzLimit;  
  ULONG MaxIdleState;  
  ULONG CurrentIdleState;
} 
PROCESSOR_POWER_INFORMATION,  *PPROCESSOR_POWER_INFORMATION;

typedef BOOL (WINAPI *GetProcessMemoryInfoType) (HANDLE Process, PROCESS_MEMORY_COUNTERS *ppsmemCounters, DWORD cb);
typedef BOOL (WINAPI *GlobalMemoryStatusExType) (LPMEMORYSTATUSEX lpBuffer);
typedef long (__stdcall *CallNtPowerInformationType) (POWER_INFORMATION_LEVEL information_level, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);

static int initialize = false;
static HMODULE psapi_dll = 0;
static HMODULE kernel32_dll = 0;
static HMODULE power_dll = 0;
static GetProcessMemoryInfoType GetProcessMemoryInfoFunction = 0;
static GlobalMemoryStatusExType GlobalMemoryStatusExFunction = 0;
static CallNtPowerInformationType CallNtPowerInformationFunction = 0;

void get_memory_information (DisplayInformation *display_information)
{
  if (initialize == false) {
    psapi_dll = LoadLibrary ("psapi.dll");
    if (psapi_dll) {
      GetProcessMemoryInfoFunction = (GetProcessMemoryInfoType) GetProcAddress (psapi_dll, "GetProcessMemoryInfo");
    }

    kernel32_dll = LoadLibrary ("kernel32.dll");
    if (kernel32_dll) {
      GlobalMemoryStatusExFunction = (GlobalMemoryStatusExType) GetProcAddress (kernel32_dll, "GlobalMemoryStatusEx");
    }
  
    initialize = true;
  }

  if (GlobalMemoryStatusExFunction) {
    MEMORYSTATUSEX memory_status;

    memory_status.dwLength = sizeof (MEMORYSTATUSEX);
    if (GlobalMemoryStatusExFunction (&memory_status)) {
      display_information -> _physical_memory = memory_status.ullTotalPhys;
      display_information -> _available_physical_memory = memory_status.ullAvailPhys;
      display_information -> _page_file_size = memory_status.ullTotalPageFile;
      display_information -> _available_page_file_size = memory_status.ullAvailPageFile;
      display_information -> _process_virtual_memory = memory_status.ullTotalVirtual;
      display_information -> _available_process_virtual_memory = memory_status.ullAvailVirtual;
      display_information -> _memory_load = memory_status.dwMemoryLoad;
    }    
  }
  else {
    MEMORYSTATUS memory_status;

    memory_status.dwLength = sizeof (MEMORYSTATUS);
    GlobalMemoryStatus (&memory_status);

    display_information -> _physical_memory = memory_status.dwTotalPhys;
    display_information -> _available_physical_memory = memory_status.dwAvailPhys;
    display_information -> _page_file_size = memory_status.dwTotalPageFile;
    display_information -> _available_page_file_size = memory_status.dwAvailPageFile;
    display_information -> _process_virtual_memory = memory_status.dwTotalVirtual;
    display_information -> _available_process_virtual_memory = memory_status.dwAvailVirtual;
    display_information -> _memory_load = memory_status.dwMemoryLoad;
  }

  if (GetProcessMemoryInfoFunction) {
    HANDLE process;
    DWORD process_id;
    PROCESS_MEMORY_COUNTERS process_memory_counters;

    process_id = GetCurrentProcessId();
    process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_id);
    if (process) {
      if (GetProcessMemoryInfoFunction (process, &process_memory_counters, sizeof (PROCESS_MEMORY_COUNTERS))) {
        display_information -> _page_fault_count =  process_memory_counters.PageFaultCount;
        display_information -> _process_memory =  process_memory_counters.WorkingSetSize;
        display_information -> _peak_process_memory = process_memory_counters.PeakWorkingSetSize;
        display_information -> _page_file_usage = process_memory_counters.PagefileUsage;
        display_information -> _peak_page_file_usage = process_memory_counters.PeakPagefileUsage;
      }

      CloseHandle(process);
    }
  }  
}

typedef union
{
  PN_uint64 long_integer;
}
LONG_INTEGER;

PN_uint64 cpu_time_function (void) {
#ifdef _WIN64
  return __rdtsc();
#else
  LONG_INTEGER long_integer;
  LONG_INTEGER *long_integer_pointer;
  
  long_integer_pointer = &long_integer;
  
  __asm
  {
      mov   ebx,[long_integer_pointer]
      rdtsc
      mov   [ebx + 0], eax
      mov   [ebx + 4], edx
  }

  return long_integer.long_integer;
#endif
}

typedef union
{
  struct
  {
    union
    {
      struct
      {
        unsigned char al;
        unsigned char ah;
      };
      unsigned int eax;
    };
    unsigned int ebx;
    unsigned int ecx;
    unsigned int edx;
  };
}
CPU_ID_REGISTERS;

typedef struct
{
  union
  {
    struct
    {
      int maximum_cpu_id_input;
      char cpu_vendor [16];
    };

    CPU_ID_REGISTERS cpu_id_registers_0;
  };

  union
  {
    CPU_ID_REGISTERS cpu_id_registers_1;

    struct
    {
      // eax
      union
      {
        unsigned int eax;
        unsigned int version_information;
        struct
        {
          unsigned int stepping_id : 4;
          unsigned int model : 4;
          unsigned int family : 4;
          unsigned int processor_type : 2;
          unsigned int reserved_0 : 2;
          unsigned int extended_model_id : 4;
          unsigned int extended_family_id : 8;
          unsigned int reserved_1 : 4;
        };
      };

      // ebx
      union
      {
        unsigned int ebx;
        struct
        {
          unsigned int brand_index : 8;
          unsigned int clflush : 8;
          unsigned int maximum_logical_processors : 8;
          unsigned int initial_apic_id : 8;
        };
      };

      // ecx
      union
      {
        unsigned int ecx;
        struct
        {
          unsigned int sse3 : 1;
          unsigned int reserved_1_to_2 : 2;
          unsigned int monitor : 1;
          unsigned int ds_cpl : 1;
          unsigned int vmx : 1;
          unsigned int reserved_6 : 1;
          unsigned int est : 1;
          unsigned int tm2 : 1;
          unsigned int reserved_9 : 1;
          unsigned int cnxt_id : 1;
          unsigned int reserved_11_to_12 : 2;
          unsigned int cmpxchg16b : 1;
          unsigned int xtpr_disable : 1;
          unsigned int reserved_15_to_31 : 17;
        };
      };

      // edx
      union
      {
        unsigned int edx;
        struct
        {
          unsigned int fpu : 1;
          unsigned int vme : 1;
          unsigned int de : 1;
          unsigned int pse : 1;
          unsigned int tsc : 1;
          unsigned int msr : 1;
          unsigned int pae : 1;
          unsigned int mce : 1;
          unsigned int cx8 : 1;
          unsigned int apic : 1;
          unsigned int reserved_10 : 1;
          unsigned int sep : 1;
          unsigned int mtrr : 1;
          unsigned int pge : 1;
          unsigned int mca : 1;
          unsigned int cmov : 1;
          unsigned int pat : 1;
          unsigned int pse_36 : 1;
          unsigned int psn : 1;
          unsigned int cflush : 1;
          unsigned int reserved_20 : 1;
          unsigned int ds : 1;
          unsigned int acpi : 1;
          unsigned int mmx : 1;
          unsigned int fxsr : 1;
          unsigned int sse : 1;
          unsigned int sse2 : 1;
          unsigned int ss : 1;
          unsigned int htt : 1;
          unsigned int tm : 1;
          unsigned int reserved_30 : 1;
          unsigned int pbe : 1;
        };
      };
    };
  };

  #define MAXIMUM_2 8
  #define MAXIMUM_CHARACTERS (MAXIMUM_2 * sizeof (CPU_ID_REGISTERS))

  union
  {
    CPU_ID_REGISTERS cpu_id_registers_2;
    unsigned char character_array_2 [MAXIMUM_CHARACTERS];
    CPU_ID_REGISTERS cpu_id_registers_2_array [MAXIMUM_2];
  };

  union
  {
    CPU_ID_REGISTERS cpu_id_registers_0x80000000;
  };

  union
  {
    CPU_ID_REGISTERS cpu_id_registers_0x80000001;
  };

  union
  {
    char cpu_brand_string [sizeof (CPU_ID_REGISTERS) * 3];
    struct 
    {
      CPU_ID_REGISTERS cpu_id_registers_0x80000002;
      CPU_ID_REGISTERS cpu_id_registers_0x80000003;
      CPU_ID_REGISTERS cpu_id_registers_0x80000004;
    };
  };

  union
  {
    struct
    {
      // eax
      union
      {
        unsigned int eax;
      };

      // ebx
      union
      {
        unsigned int ebx;
      };

      // ecx
      union
      {
        unsigned int ecx;
        struct
        {
          unsigned int l1_data_cache_line_size : 8;
          unsigned int l1_data_reserved_8_to_15 : 8;
          unsigned int l1_data_associativity : 8;
          unsigned int l1_data_cache_size : 8;
        };
      };

      // edx
      union
      {
        unsigned int edx;
        struct
        {
          unsigned int l1_code_cache_line_size : 8;
          unsigned int l1_code_reserved_8_to_15 : 8;
          unsigned int l1_code_associativity : 8;
          unsigned int l1_code_cache_size : 8;
        };
      };
    };
    CPU_ID_REGISTERS cpu_id_registers_0x80000005;
  };

  union
  {
    struct
    {
      // eax
      union
      {
        unsigned int eax;
      };

      // ebx
      union
      {
        unsigned int ebx;
      };

      // ecx
      union
      {
        unsigned int ecx;
        struct
        {
          unsigned int l2_cache_line_size : 8;
          unsigned int l2_reserved_8_to_11 : 4;
          unsigned int l2_associativity : 4;
          unsigned int l2_cache_size : 16;
        };
      };

      // edx
      union
      {
        unsigned int edx;
      };
    };
    CPU_ID_REGISTERS cpu_id_registers_0x80000006;
  };

  union
  {
    struct
    {
      // eax
      union
      {
        unsigned int eax;
      };

      // ebx
      union
      {
        unsigned int ebx;
      };

      // ecx
      union
      {
        unsigned int ecx;
      };

      // edx
      union
      {
        unsigned int edx;
      };
    };
    CPU_ID_REGISTERS cpu_id_registers_0x80000008;
  };
  
  unsigned int cache_line_size;
  unsigned int log_base_2_cache_line_size;
}
CPU_ID;

typedef struct
{
  CPU_ID_REGISTERS cpu_id_registers_0;
  CPU_ID_REGISTERS cpu_id_registers_1;

  CPU_ID_REGISTERS cpu_id_registers_0x80000000;
  CPU_ID_REGISTERS cpu_id_registers_0x80000001;
  CPU_ID_REGISTERS cpu_id_registers_0x80000002;
  CPU_ID_REGISTERS cpu_id_registers_0x80000003;
  CPU_ID_REGISTERS cpu_id_registers_0x80000004;

  CPU_ID_REGISTERS cpu_id_registers_0x80000006;

  CPU_ID_REGISTERS cpu_id_registers_0x80000008;
}
CPU_ID_BINARY_DATA;

typedef struct
{
  union
  {
    CPU_ID_BINARY_DATA cpu_binary_data;
    unsigned int data_array [sizeof (CPU_ID_BINARY_DATA) / 4];
  };
}
CPU_ID_BINARY_DATA_ARRAY;

void cpu_id_to_cpu_id_binary_data (CPU_ID *cpu_id, CPU_ID_BINARY_DATA *cpu_id_binary_data) {

  cpu_id_binary_data -> cpu_id_registers_0 = cpu_id -> cpu_id_registers_0;
  cpu_id_binary_data -> cpu_id_registers_1 = cpu_id -> cpu_id_registers_1;
  cpu_id_binary_data -> cpu_id_registers_0x80000000 = cpu_id -> cpu_id_registers_0x80000000;
  cpu_id_binary_data -> cpu_id_registers_0x80000001 = cpu_id -> cpu_id_registers_0x80000001;
  cpu_id_binary_data -> cpu_id_registers_0x80000002 = cpu_id -> cpu_id_registers_0x80000002;
  cpu_id_binary_data -> cpu_id_registers_0x80000003 = cpu_id -> cpu_id_registers_0x80000003;
  cpu_id_binary_data -> cpu_id_registers_0x80000004 = cpu_id -> cpu_id_registers_0x80000004;
  cpu_id_binary_data -> cpu_id_registers_0x80000006 = cpu_id -> cpu_id_registers_0x80000006;
  cpu_id_binary_data -> cpu_id_registers_0x80000008 = cpu_id -> cpu_id_registers_0x80000008;
}

void cpu_id_binary_data_to_cpu_id (CPU_ID_BINARY_DATA *cpu_id_binary_data, CPU_ID *cpu_id) {

  memset (cpu_id, 0, sizeof (CPU_ID));

  cpu_id -> cpu_id_registers_0 = cpu_id_binary_data -> cpu_id_registers_0;
  cpu_id -> cpu_id_registers_1 = cpu_id_binary_data -> cpu_id_registers_1;
  cpu_id -> cpu_id_registers_0x80000000 = cpu_id_binary_data -> cpu_id_registers_0x80000000;
  cpu_id -> cpu_id_registers_0x80000001 = cpu_id_binary_data -> cpu_id_registers_0x80000001;
  cpu_id -> cpu_id_registers_0x80000002 = cpu_id_binary_data -> cpu_id_registers_0x80000002;
  cpu_id -> cpu_id_registers_0x80000003 = cpu_id_binary_data -> cpu_id_registers_0x80000003;
  cpu_id -> cpu_id_registers_0x80000004 = cpu_id_binary_data -> cpu_id_registers_0x80000004;
  cpu_id -> cpu_id_registers_0x80000006 = cpu_id_binary_data -> cpu_id_registers_0x80000006;
  cpu_id -> cpu_id_registers_0x80000008 = cpu_id_binary_data -> cpu_id_registers_0x80000008;
}

int cpuid (int input_eax, CPU_ID_REGISTERS *cpu_id_registers) {
  int state;

  state = false;   
  __try
  {
    if (input_eax == 0) {
      // the order of ecx and edx is swapped when saved to make a proper vendor string
#ifdef _WIN64
        __cpuid((int*)cpu_id_registers, input_eax);
        unsigned int tmp = cpu_id_registers->edx;
        cpu_id_registers->edx = cpu_id_registers->ecx;
        cpu_id_registers->ecx = tmp;
#else
      __asm
      {
          mov   eax, [input_eax]
          mov   edi, [cpu_id_registers]

          cpuid

          mov   [edi + 0], eax
          mov   [edi + 4], ebx
          mov   [edi + 8], edx
          mov   [edi + 12], ecx
      }
#endif
    }
    else {
#ifdef _WIN64
        __cpuid((int*)cpu_id_registers, input_eax);
#else
      __asm
      {
          mov   eax, [input_eax]
          mov   edi, [cpu_id_registers]

          cpuid

          mov   [edi + 0], eax
          mov   [edi + 4], ebx
          mov   [edi + 8], ecx
          mov   [edi + 12], edx
      }
#endif
    }

    state = true;
  }
  __except (1)
  {
    state = false;
  }

  return state;
}

void parse_cpu_id (CPU_ID *cpu_id) {

  printf ("CPUID\n");
  printf ("  vendor = %s \n", cpu_id -> cpu_vendor);
  printf ("  brand string %s \n", cpu_id -> cpu_brand_string);
  printf ("  maximum_cpu_id_input = %u \n", cpu_id -> maximum_cpu_id_input);
  printf ("  maximum extended information = 0x%X \n", cpu_id -> cpu_id_registers_0x80000000.eax);  

  printf ("  MMX  = %u \n", cpu_id -> mmx);
  printf ("  SSE  = %u \n", cpu_id -> sse);
  printf ("  SSE2 = %u \n", cpu_id -> sse2);
  printf ("  SSE3 = %u \n", cpu_id -> sse3);

  printf ("  EST  = %u \n", cpu_id -> est);

  if (cpu_id -> maximum_cpu_id_input >= 1) {
    printf ("  version_information \n");
    printf ("    stepping_id %u \n", cpu_id -> stepping_id);
    printf ("    model %u \n", cpu_id -> model);
    printf ("    family %u \n", cpu_id -> family);
    printf ("    processor_type %u \n", cpu_id -> processor_type);
    printf ("    extended_model_id %u \n", cpu_id -> extended_model_id);
    printf ("    extended_family_id %u \n", cpu_id -> extended_family_id);

    printf ("    brand_index %u \n", cpu_id -> brand_index);
    printf ("    clflush %u \n", cpu_id -> clflush);
    printf ("    maximum_logical_processors %u \n", cpu_id -> maximum_logical_processors);
    printf ("    initial_apic_id %u \n", cpu_id -> initial_apic_id);

//    printf ("  cache_line_size %u \n", cpu_id -> cache_line_size);
//    printf ("  log_base_2_cache_line_size %u \n", cpu_id -> log_base_2_cache_line_size);
  }

  if (cpu_id -> cpu_id_registers_0x80000000.eax >= 0x80000005) {
    printf ("  l1_data_cache_line_size %d \n", cpu_id -> l1_data_cache_line_size);
    printf ("  l1_data_associativity %d \n", cpu_id -> l1_data_associativity);
    printf ("  l1_data_cache_size %dK \n", cpu_id -> l1_data_cache_size);       

    printf ("  l1_code_cache_line_size %d \n", cpu_id -> l1_code_cache_line_size);
    printf ("  l1_code_associativity %d \n", cpu_id -> l1_code_associativity);
    printf ("  l1_code_cache_size %dK \n", cpu_id -> l1_code_cache_size);
  }

  if (cpu_id -> cpu_id_registers_0x80000000.eax >= 0x80000006) {
    printf ("  l2_cache_line_size %d \n", cpu_id -> l2_cache_line_size);
    printf ("  l2_associativity %d \n", cpu_id -> l2_associativity);
    printf ("  l2_cache_size %dK \n", cpu_id -> l2_cache_size);       
  }
}

int initialize_cpu_id (CPU_ID *cpu_id) {

  int state;
  int debug;
  
  state = false;
  debug = false;
  memset (cpu_id, 0, sizeof (CPU_ID));

  if (cpuid (0, &cpu_id -> cpu_id_registers_0)) {
    if (cpu_id -> maximum_cpu_id_input >= 1) {
      cpuid (1, &cpu_id -> cpu_id_registers_1);
    }
    if (cpu_id -> maximum_cpu_id_input >= 2) {
      unsigned int index;

      cpuid (2, &cpu_id -> cpu_id_registers_2);
      if (debug) {
        printf ("  al = %u \n", cpu_id -> cpu_id_registers_2.al);
      }
      
      for (index = 1; index < cpu_id -> cpu_id_registers_2.al && index < MAXIMUM_2; index++) {
        cpuid (2, &cpu_id -> cpu_id_registers_2_array [index]);
      }

      for (index = 1; index < MAXIMUM_CHARACTERS; index++) {
        if (cpu_id -> character_array_2 [index]) {
          if (debug) {
            printf ("  cache/TLB byte = %X \n", cpu_id -> character_array_2 [index]);
          }
          switch (cpu_id -> character_array_2 [index])
          {
            case 0x0A:
            case 0x0C:
              cpu_id -> cache_line_size = 32;
              cpu_id -> log_base_2_cache_line_size = 5;
              break;

            case 0x2C:
            case 0x60:
            case 0x66:
            case 0x67:
            case 0x68:
              cpu_id -> cache_line_size = 64;
              cpu_id -> log_base_2_cache_line_size = 6;
              break;
          }
        }
      }
    }

    cpuid (0x80000000, &cpu_id -> cpu_id_registers_0x80000000);  

    if (cpu_id -> cpu_id_registers_0x80000000.eax >= 0x80000001) {
      cpuid (0x80000001, &cpu_id -> cpu_id_registers_0x80000001);  
    }
    
    if (cpu_id -> cpu_id_registers_0x80000000.eax >= 0x80000004) {
      cpuid (0x80000002, &cpu_id -> cpu_id_registers_0x80000002);  
      cpuid (0x80000003, &cpu_id -> cpu_id_registers_0x80000003);  
      cpuid (0x80000004, &cpu_id -> cpu_id_registers_0x80000004);  
    }

    if (cpu_id -> cpu_id_registers_0x80000000.eax >= 0x80000005) {
      cpuid (0x80000005, &cpu_id -> cpu_id_registers_0x80000005);  
    }

    if (cpu_id -> cpu_id_registers_0x80000000.eax >= 0x80000006) {
      cpuid (0x80000006, &cpu_id -> cpu_id_registers_0x80000006);  
    }

    if (cpu_id -> cpu_id_registers_0x80000000.eax >= 0x80000008) {
      cpuid (0x80000008, &cpu_id -> cpu_id_registers_0x80000008);  
    }    

    state = true;
  }
  
  return state;
}

int update_cpu_frequency_function (int processor_number, DisplayInformation *display_information)
{
  int update;
  
  update = false;
  display_information -> _maximum_cpu_frequency = 0;
  display_information -> _current_cpu_frequency = 0;
  
  if (CallNtPowerInformationFunction) {

    int i;
    PVOID input_buffer;
    PVOID output_buffer;
    ULONG input_buffer_size;
    ULONG output_buffer_size;
    POWER_INFORMATION_LEVEL information_level;
    PROCESSOR_POWER_INFORMATION *processor_power_information;
    PROCESSOR_POWER_INFORMATION processor_power_information_array [MAXIMUM_PROCESSORS];
 
    memset (processor_power_information_array, 0, sizeof (PROCESSOR_POWER_INFORMATION) * MAXIMUM_PROCESSORS);

    processor_power_information = processor_power_information_array;
    for (i = 0; i < MAXIMUM_PROCESSORS; i++) {
      processor_power_information -> Number = 0xFFFFFFFF;
      processor_power_information++;
    }
    
    information_level = ProcessorInformation;
    input_buffer = NULL;
    output_buffer = processor_power_information_array;
    input_buffer_size = 0;
    output_buffer_size = sizeof (PROCESSOR_POWER_INFORMATION) * MAXIMUM_PROCESSORS;
    if (CallNtPowerInformationFunction (information_level, input_buffer, input_buffer_size, output_buffer, output_buffer_size) == 0) {
      processor_power_information = processor_power_information_array;
      for (i = 0; i < MAXIMUM_PROCESSORS; i++) {
        if (processor_power_information -> Number == processor_number) {
          PN_uint64 value;

          value = processor_power_information -> MaxMhz;
          display_information -> _maximum_cpu_frequency = value * 1000000;

          value = processor_power_information -> CurrentMhz;     
          display_information -> _current_cpu_frequency = value * 1000000;
          update = true;

          break;
        }

        processor_power_information++;
      }      
    }
  }

  return update;
}

void
count_number_of_cpus(DisplayInformation *display_information) {
  int num_cpu_cores = 0;
  int num_logical_cpus = 0;

  // Get a pointer to the GetLogicalProcessorInformation function.
  typedef BOOL (WINAPI *LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, 
                                   PDWORD);
  LPFN_GLPI glpi;
  glpi = (LPFN_GLPI)GetProcAddress(GetModuleHandle(TEXT("kernel32")),
                                    "GetLogicalProcessorInformation");
  if (glpi == NULL) {
    windisplay_cat.info()
      << "GetLogicalProcessorInformation is not supported.\n";
    return;
  }

  // Allocate a buffer to hold the result of the
  // GetLogicalProcessorInformation call.
  PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL;
  DWORD buffer_length = 0;
  DWORD rc = glpi(buffer, &buffer_length);
  while (!rc) {
    if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
      if (buffer != NULL) {
        PANDA_FREE_ARRAY(buffer);
      }
      
      buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)PANDA_MALLOC_ARRAY(buffer_length);
      nassertv(buffer != NULL);
    } else {
      windisplay_cat.info()
        << "GetLogicalProcessorInformation failed: " << GetLastError()
        << "\n";
      return;
    }
    rc = glpi(buffer, &buffer_length);
  }

  // Now get the results.
  PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = buffer;
  PSYSTEM_LOGICAL_PROCESSOR_INFORMATION end = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)((char *)buffer + buffer_length);

  while (ptr < end) {
    if (ptr->Relationship == RelationProcessorCore) {
      num_cpu_cores++;
      
      // A hyperthreaded core supplies more than one logical processor.
      num_logical_cpus += count_bits_in_word((PN_uint64)(ptr->ProcessorMask));
    }
    ++ptr;
  }

  PANDA_FREE_ARRAY(buffer);

  windisplay_cat.info()
    << num_cpu_cores << " CPU cores, with "
    << num_logical_cpus << " logical processors.\n";

  display_information->_num_cpu_cores = num_cpu_cores;
  display_information->_num_logical_cpus = num_logical_cpus;
}


////////////////////////////////////////////////////////////////////
//     Function: WinGraphicsPipe::Constructor
//       Access: Public
//  Description: 
////////////////////////////////////////////////////////////////////
WinGraphicsPipe::
WinGraphicsPipe() {

  bool state;
  char string [512];
  
  state = false;
  _supported_types = OT_window | OT_fullscreen_window;

  // these fns arent defined on win95, so get dynamic ptrs to them
  // to avoid ugly DLL loader failures on w95
  _pfnTrackMouseEvent = NULL;

  _hUser32 = (HINSTANCE)LoadLibrary("user32.dll");
  if (_hUser32 != NULL) {
    _pfnTrackMouseEvent = 
      (PFN_TRACKMOUSEEVENT)GetProcAddress(_hUser32, "TrackMouseEvent");
  }

#ifdef HAVE_DX9
  if (request_dxdisplay_information){
    DisplaySearchParameters display_search_parameters_dx9;
    int dx9_display_information (DisplaySearchParameters &display_search_parameters_dx9, DisplayInformation *display_information);

    if (state == false && dx9_display_information (display_search_parameters_dx9, _display_information)) {
      state = true;
    }
  }
#endif

#ifdef HAVE_DX8
  if (request_dxdisplay_information){
    DisplaySearchParameters display_search_parameters_dx8;
    int dx8_display_information (DisplaySearchParameters &display_search_parameters_dx8, DisplayInformation *display_information);
    
    if (state == false && dx8_display_information (display_search_parameters_dx8, _display_information)) {
      state = true;    
    }
  }
#endif

  if (auto_cpu_data) {
    lookup_cpu_data();
  }

  OSVERSIONINFO version_info;

  version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  if (GetVersionEx (&version_info)) {
    sprintf (string, "OS version: %d.%d.%d.%d \n", version_info.dwMajorVersion, version_info.dwMinorVersion, version_info.dwPlatformId, version_info.dwBuildNumber);
    windisplay_cat.info() << string;
    windisplay_cat.info() << "  " << version_info.szCSDVersion << "\n";
    
    _display_information -> _os_version_major = version_info.dwMajorVersion;
    _display_information -> _os_version_minor = version_info.dwMinorVersion;
    _display_information -> _os_version_build = version_info.dwBuildNumber;     
    _display_information -> _os_platform_id = version_info.dwPlatformId;
  }
  // Screen size
  _display_width = GetSystemMetrics(SM_CXSCREEN);
  _display_height = GetSystemMetrics(SM_CYSCREEN);
  
  HMODULE power_dll;

  power_dll = LoadLibrary ("PowrProf.dll");
  if (power_dll) {    
    CallNtPowerInformationFunction = (CallNtPowerInformationType) GetProcAddress (power_dll, "CallNtPowerInformation");
    if (CallNtPowerInformationFunction) {    
    
      _display_information -> _update_cpu_frequency_function = update_cpu_frequency_function;
      update_cpu_frequency_function(0, _display_information);

      sprintf (string, "max Mhz %I64d, current Mhz %I64d \n", _display_information -> _maximum_cpu_frequency, _display_information -> _current_cpu_frequency);     

      windisplay_cat.info() << string;    
    }
  }
  
  if (state) {

  }
}

////////////////////////////////////////////////////////////////////
//     Function: WinGraphicsPipe::lookup_cpu_data
//       Access: Public, Virtual
//  Description: Looks up the detailed CPU information and stores it
//               in _display_information, if supported by the OS.
//               This may take a second or two.
////////////////////////////////////////////////////////////////////
void WinGraphicsPipe::
lookup_cpu_data() {
  char string [512];

  // set callback for memory function
  _display_information -> _get_memory_information_function = get_memory_information;

  // set callback for cpu time function
  _display_information -> _cpu_time_function = cpu_time_function;

  // determine CPU frequency
  PN_uint64 time;
  PN_uint64 end_time;
  LARGE_INTEGER counter;
  LARGE_INTEGER end;
  LARGE_INTEGER frequency;

  time = 0;
  end_time = 0;
  counter.QuadPart = 0;
  end.QuadPart = 0;
  frequency.QuadPart = 0;
  
  int priority;
  HANDLE thread;

  windisplay_cat.info() << "begin QueryPerformanceFrequency\n";
  thread = GetCurrentThread();
  priority = GetThreadPriority (thread);
  SetThreadPriority(thread, THREAD_PRIORITY_TIME_CRITICAL);
  
  if (QueryPerformanceFrequency(&frequency)) {    
    if (frequency.QuadPart > 0) {
      if (QueryPerformanceCounter (&counter)) {
        time = cpu_time_function();
        end.QuadPart = counter.QuadPart + frequency.QuadPart;
        while (QueryPerformanceCounter (&counter) && counter.QuadPart < end.QuadPart) {

        }
        end_time = cpu_time_function();

        _display_information -> _cpu_frequency = end_time - time;
      }
    }
  }

  SetThreadPriority(thread, priority);
  sprintf (string, "QueryPerformanceFrequency: %I64d\n", frequency.QuadPart);    
  windisplay_cat.info() << string;
  sprintf (string, "CPU frequency: %I64d\n", _display_information -> _cpu_frequency);
  windisplay_cat.info() << string;


  // CPUID
  CPU_ID cpu_id;

  windisplay_cat.info() << "start CPU ID\n";
  
  if (initialize_cpu_id (&cpu_id)) {  
    CPU_ID_BINARY_DATA *cpu_id_binary_data;
    
    cpu_id_binary_data = new (CPU_ID_BINARY_DATA);    
    if (cpu_id_binary_data) {  
      cpu_id_to_cpu_id_binary_data (&cpu_id, cpu_id_binary_data);
      _display_information -> _cpu_id_size = sizeof (CPU_ID_BINARY_DATA) / sizeof (unsigned int);
      _display_information -> _cpu_id_data = (unsigned int *) cpu_id_binary_data;

      _display_information -> _cpu_vendor_string = strdup(cpu_id.cpu_vendor);
      _display_information -> _cpu_brand_string = strdup(cpu_id.cpu_brand_string);
      _display_information -> _cpu_version_information = cpu_id.version_information;
      _display_information -> _cpu_brand_index = cpu_id.brand_index;

      if (windisplay_cat.is_debug()) {
        windisplay_cat.debug()
          << hex << _display_information -> _cpu_id_version << dec << "|";

        int index;
        for (index = 0; index < _display_information -> _cpu_id_size; index++) {
          unsigned int data;

          data = _display_information -> _cpu_id_data [index];

          windisplay_cat.debug(false)
            << hex << data << dec;
          if (index < (_display_information -> _cpu_id_size - 1)) {
            windisplay_cat.debug(false)
              << "|";
          }
        }
        windisplay_cat.debug(false)
          << "\n";
      }
    }

    if (windisplay_cat.is_debug()) {
      parse_cpu_id (&cpu_id);
    }
  }

  windisplay_cat.info() << "end CPU ID\n";

  // Number of CPU's
  count_number_of_cpus(_display_information);
}

////////////////////////////////////////////////////////////////////
//     Function: WinGraphicsPipe::Destructor
//       Access: Public, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
WinGraphicsPipe::
~WinGraphicsPipe() {
  if (_hUser32 != NULL) {
    FreeLibrary(_hUser32);
    _hUser32 = NULL;
  }
}

bool MyGetProcAddr(HINSTANCE hDLL, FARPROC *pFn, const char *szExportedFnName) {
  *pFn = (FARPROC) GetProcAddress(hDLL, szExportedFnName);
  if (*pFn == NULL) {
    windisplay_cat.error() << "GetProcAddr failed for " << szExportedFnName << ", error=" << GetLastError() <<endl;
    return false;
  }
  return true;
}

bool MyLoadLib(HINSTANCE &hDLL, const char *DLLname) {
  hDLL = LoadLibrary(DLLname);
  if(hDLL == NULL) {
    windisplay_cat.error() << "LoadLibrary failed for " << DLLname << ", error=" << GetLastError() <<endl;
    return false;
  }
  return true;
}
