mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-01 01:07:51 -04:00
Merge branch 'release/1.10.x'
This commit is contained in:
commit
cbb3c182a5
@ -22,7 +22,7 @@ Installing Panda3D
|
|||||||
==================
|
==================
|
||||||
|
|
||||||
The latest Panda3D SDK can be downloaded from
|
The latest Panda3D SDK can be downloaded from
|
||||||
[this page](https://www.panda3d.org/download/sdk-1-10-0/).
|
[this page](https://www.panda3d.org/download/sdk-1-10-1/).
|
||||||
If you are familiar with installing Python packages, you can use
|
If you are familiar with installing Python packages, you can use
|
||||||
the following comand:
|
the following comand:
|
||||||
|
|
||||||
|
@ -100,8 +100,10 @@ class DirectScrolledFrame(DirectFrame):
|
|||||||
simpleChildGui = self.guiDict.get(parts[-1])
|
simpleChildGui = self.guiDict.get(parts[-1])
|
||||||
if simpleChildGui:
|
if simpleChildGui:
|
||||||
simpleChildGui.destroy()
|
simpleChildGui.destroy()
|
||||||
self.verticalScroll.destroy()
|
if self.verticalScroll:
|
||||||
self.horizontalScroll.destroy()
|
self.verticalScroll.destroy()
|
||||||
del self.verticalScroll
|
if self.horizontalScroll:
|
||||||
del self.horizontalScroll
|
self.horizontalScroll.destroy()
|
||||||
|
self.verticalScroll = None
|
||||||
|
self.horizontalScroll = None
|
||||||
DirectFrame.destroy(self)
|
DirectFrame.destroy(self)
|
||||||
|
@ -96,22 +96,21 @@ class ParticlePanel(AppShell):
|
|||||||
|
|
||||||
## MENUBAR ENTRIES ##
|
## MENUBAR ENTRIES ##
|
||||||
# FILE MENU
|
# FILE MENU
|
||||||
# Get a handle on the file menu so commands can be inserted
|
# Get a handle on the file menu, and delete the Quit item that AppShell
|
||||||
# before quit item
|
# created so we can add it back after adding the other items.
|
||||||
fileMenu = self.menuBar.component('File-menu')
|
self.menuBar.deletemenuitems('File', 0, 0)
|
||||||
# MRM: Need to add load and save effects methods
|
self.menuBar.addmenuitem('File', 'command',
|
||||||
fileMenu.insert_command(
|
label='Load Params',
|
||||||
fileMenu.index('Quit'),
|
command=self.loadParticleEffectFromFile)
|
||||||
label = 'Load Params',
|
self.menuBar.addmenuitem('File', 'command',
|
||||||
command = self.loadParticleEffectFromFile)
|
label='Save Params',
|
||||||
fileMenu.insert_command(
|
command=self.saveParticleEffectToFile)
|
||||||
fileMenu.index('Quit'),
|
self.menuBar.addmenuitem('File', 'command',
|
||||||
label = 'Save Params',
|
label='Print Params',
|
||||||
command = self.saveParticleEffectToFile)
|
command=lambda s=self:s.particles.printParams())
|
||||||
fileMenu.insert_command(
|
self.menuBar.addmenuitem('File', 'command', 'Quit this application',
|
||||||
fileMenu.index('Quit'),
|
label='Quit',
|
||||||
label = 'Print Params',
|
command=self.quit)
|
||||||
command = lambda s = self: s.particles.printParams())
|
|
||||||
|
|
||||||
# PARTICLE MANAGER MENU
|
# PARTICLE MANAGER MENU
|
||||||
self.menuBar.addmenu('ParticleMgr', 'ParticleMgr Operations')
|
self.menuBar.addmenu('ParticleMgr', 'ParticleMgr Operations')
|
||||||
|
@ -397,17 +397,25 @@ get_call_str(const string &container, const vector_string &pexprs) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// It's not possible to assign arrays in C++, we have to copy them.
|
// It's not possible to assign arrays in C++, we have to copy them.
|
||||||
CPPArrayType *array_type = _parameters[_first_true_parameter]._remap->get_orig_type()->as_array_type();
|
bool paren_close = false;
|
||||||
|
CPPType *param_type = _parameters[_first_true_parameter]._remap->get_orig_type();
|
||||||
|
CPPArrayType *array_type = param_type->as_array_type();
|
||||||
if (array_type != nullptr) {
|
if (array_type != nullptr) {
|
||||||
call << "std::copy(" << expr << ", " << expr << " + " << *array_type->_bounds << ", ";
|
call << "std::copy(" << expr << ", " << expr << " + " << *array_type->_bounds << ", ";
|
||||||
} else {
|
paren_close = true;
|
||||||
|
}
|
||||||
|
else if (TypeManager::is_pointer_to_PyObject(param_type)) {
|
||||||
|
call << "Dtool_Assign_PyObject(" << expr << ", ";
|
||||||
|
paren_close = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
call << expr << " = ";
|
call << expr << " = ";
|
||||||
}
|
}
|
||||||
|
|
||||||
_parameters[_first_true_parameter]._remap->pass_parameter(call,
|
_parameters[_first_true_parameter]._remap->pass_parameter(call,
|
||||||
get_parameter_expr(_first_true_parameter, pexprs));
|
get_parameter_expr(_first_true_parameter, pexprs));
|
||||||
|
|
||||||
if (array_type != nullptr) {
|
if (paren_close) {
|
||||||
call << ')';
|
call << ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,6 +780,11 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
|
|||||||
_return_value_destructor = builder.get_destructor_for(return_meat_type);
|
_return_value_destructor = builder.get_destructor_for(return_meat_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_type == T_getter && TypeManager::is_pointer_to_PyObject(return_type)) {
|
||||||
|
_manage_reference_count = true;
|
||||||
|
_return_value_needs_management = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for a special meaning by name and signature.
|
// Check for a special meaning by name and signature.
|
||||||
size_t first_param = 0;
|
size_t first_param = 0;
|
||||||
if (_has_this) {
|
if (_has_this) {
|
||||||
|
@ -1690,7 +1690,6 @@ write_module_class(ostream &out, Object *obj) {
|
|||||||
SlottedFunctionDef def;
|
SlottedFunctionDef def;
|
||||||
def._answer_location = true_key;
|
def._answer_location = true_key;
|
||||||
def._wrapper_type = slotted_def._wrapper_type;
|
def._wrapper_type = slotted_def._wrapper_type;
|
||||||
def._min_version = 0x03000000;
|
|
||||||
def._wrapper_name = func->_name + "_" + true_key;
|
def._wrapper_name = func->_name + "_" + true_key;
|
||||||
slots[true_key] = def;
|
slots[true_key] = def;
|
||||||
}
|
}
|
||||||
@ -5993,7 +5992,11 @@ write_function_instance(ostream &out, FunctionRemap *remap,
|
|||||||
indent(out, indent_level) << "}\n";
|
indent(out, indent_level) << "}\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
return_expr = manage_return_value(out, indent_level, remap, "return_value");
|
if (TypeManager::is_pointer_to_PyObject(remap->_return_type->get_orig_type())) {
|
||||||
|
indent(out, indent_level) << "Py_XINCREF(return_value);\n";
|
||||||
|
} else {
|
||||||
|
return_expr = manage_return_value(out, indent_level, remap, "return_value");
|
||||||
|
}
|
||||||
return_expr = remap->_return_type->temporary_to_return(return_expr);
|
return_expr = remap->_return_type->temporary_to_return(return_expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +90,19 @@ INLINE PyObject *DtoolInstance_RichComparePointers(PyObject *v1, PyObject *v2, i
|
|||||||
Py_RETURN_RICHCOMPARE(cmpval, 0, op);
|
Py_RETURN_RICHCOMPARE(cmpval, 0, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility function for assigning a PyObject pointer while managing refcounts.
|
||||||
|
*/
|
||||||
|
ALWAYS_INLINE void
|
||||||
|
Dtool_Assign_PyObject(PyObject *&ptr, PyObject *value) {
|
||||||
|
PyObject *prev_value = ptr;
|
||||||
|
if (prev_value != value) {
|
||||||
|
Py_XINCREF(value);
|
||||||
|
ptr = value;
|
||||||
|
Py_XDECREF(prev_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the enum value to a C long.
|
* Converts the enum value to a C long.
|
||||||
*/
|
*/
|
||||||
|
@ -238,6 +238,8 @@ EXPCL_PYPANDA PyObject *_Dtool_Return(PyObject *value);
|
|||||||
#define Dtool_Return(value) _Dtool_Return(value)
|
#define Dtool_Return(value) _Dtool_Return(value)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ALWAYS_INLINE void Dtool_Assign_PyObject(PyObject *&ptr, PyObject *value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around Python 3.4's enum library, which does not have a C API.
|
* Wrapper around Python 3.4's enum library, which does not have a C API.
|
||||||
*/
|
*/
|
||||||
|
@ -157,7 +157,7 @@ def usage(problem):
|
|||||||
print(" --everything (enable every third-party lib)")
|
print(" --everything (enable every third-party lib)")
|
||||||
print(" --directx-sdk=X (specify version of DirectX SDK to use: jun2010, aug2009, mar2009, aug2006)")
|
print(" --directx-sdk=X (specify version of DirectX SDK to use: jun2010, aug2009, mar2009, aug2006)")
|
||||||
print(" --windows-sdk=X (specify Windows SDK version, eg. 7.0, 7.1 or 10. Default is 7.1)")
|
print(" --windows-sdk=X (specify Windows SDK version, eg. 7.0, 7.1 or 10. Default is 7.1)")
|
||||||
print(" --msvc-version=X (specify Visual C++ version, eg. 10, 11, 12, 14. Default is 14)")
|
print(" --msvc-version=X (specify Visual C++ version, eg. 10, 11, 12, 14, 14.1, 14.2. Default is 14)")
|
||||||
print(" --use-icl (experimental setting to use an intel compiler instead of MSVC on Windows)")
|
print(" --use-icl (experimental setting to use an intel compiler instead of MSVC on Windows)")
|
||||||
print("")
|
print("")
|
||||||
print("The simplest way to compile panda is to just type:")
|
print("The simplest way to compile panda is to just type:")
|
||||||
|
@ -78,6 +78,7 @@ MSVCVERSIONINFO = {
|
|||||||
(12,0): {"vsversion":(12,0), "vsname":"Visual Studio 2013"},
|
(12,0): {"vsversion":(12,0), "vsname":"Visual Studio 2013"},
|
||||||
(14,0): {"vsversion":(14,0), "vsname":"Visual Studio 2015"},
|
(14,0): {"vsversion":(14,0), "vsname":"Visual Studio 2015"},
|
||||||
(14,1): {"vsversion":(15,0), "vsname":"Visual Studio 2017"},
|
(14,1): {"vsversion":(15,0), "vsname":"Visual Studio 2017"},
|
||||||
|
(14,2): {"vsversion":(16,0), "vsname":"Visual Studio 2019"},
|
||||||
}
|
}
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
|
@ -120,7 +120,7 @@ do_set_mass(PN_stdfloat mass) {
|
|||||||
btScalar bt_mass = mass;
|
btScalar bt_mass = mass;
|
||||||
btVector3 bt_inertia(0.0, 0.0, 0.0);
|
btVector3 bt_inertia(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
if (bt_mass > 0.0) {
|
if (bt_mass > 0.0 && !_shapes.empty()) {
|
||||||
_rigid->getCollisionShape()->calculateLocalInertia(bt_mass, bt_inertia);
|
_rigid->getCollisionShape()->calculateLocalInertia(bt_mass, bt_inertia);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class BulletPersistentManifold;
|
|||||||
class BulletShape;
|
class BulletShape;
|
||||||
class BulletSoftBodyWorldInfo;
|
class BulletSoftBodyWorldInfo;
|
||||||
|
|
||||||
extern PT(CallbackObject) bullet_contact_added_callback;
|
extern EXPCL_PANDABULLET PT(CallbackObject) bullet_contact_added_callback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -711,8 +711,11 @@ close_window() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_window != nil) {
|
if (_window != nil) {
|
||||||
[_window setReleasedWhenClosed: YES];
|
|
||||||
[_window close];
|
[_window close];
|
||||||
|
|
||||||
|
// Process events once more so any pending NSEvents are cleared. Not doing
|
||||||
|
// this causes the window to stick around after calling [_window close].
|
||||||
|
process_events();
|
||||||
_window = nil;
|
_window = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ class CocoaGraphicsWindow;
|
|||||||
- (void)windowDidBecomeKey:(NSNotification *)notification;
|
- (void)windowDidBecomeKey:(NSNotification *)notification;
|
||||||
- (void)windowDidResignKey:(NSNotification *)notification;
|
- (void)windowDidResignKey:(NSNotification *)notification;
|
||||||
- (BOOL)windowShouldClose:(id)sender;
|
- (BOOL)windowShouldClose:(id)sender;
|
||||||
- (void)windowWillClose:(NSNotification *)notification;
|
|
||||||
|
|
||||||
// TODO: handle fullscreen on Lion.
|
// TODO: handle fullscreen on Lion.
|
||||||
|
|
||||||
|
@ -50,11 +50,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) windowShouldClose:(id)sender {
|
- (BOOL) windowShouldClose:(id)sender {
|
||||||
return _graphicsWindow->handle_close_request();
|
bool should_close = _graphicsWindow->handle_close_request();
|
||||||
}
|
if (should_close) {
|
||||||
|
_graphicsWindow->handle_close_event();
|
||||||
- (void) windowWillClose:(NSNotification *)notification {
|
}
|
||||||
_graphicsWindow->handle_close_event();
|
return should_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -17,6 +17,22 @@
|
|||||||
|
|
||||||
#if defined(_WIN32) && !defined(CPPPARSER)
|
#if defined(_WIN32) && !defined(CPPPARSER)
|
||||||
|
|
||||||
|
#ifdef HAVE_THREADS
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class InputThread : public Thread {
|
||||||
|
public:
|
||||||
|
InputThread(WinInputDeviceManager *manager) :
|
||||||
|
Thread("input", "input"), _manager(manager) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void thread_main();
|
||||||
|
|
||||||
|
WinInputDeviceManager *_manager;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the input device manager by scanning which devices are currently
|
* Initializes the input device manager by scanning which devices are currently
|
||||||
* connected and setting up any platform-dependent structures necessary for
|
* connected and setting up any platform-dependent structures necessary for
|
||||||
@ -37,7 +53,7 @@ WinInputDeviceManager() :
|
|||||||
_xinput_device2.local_object();
|
_xinput_device2.local_object();
|
||||||
_xinput_device3.local_object();
|
_xinput_device3.local_object();
|
||||||
|
|
||||||
// This function is only available in Vista and later, so we use a wrapper.
|
// This function is only available in Vista and later, so we use a wrapper.
|
||||||
HMODULE module = LoadLibraryA("cfgmgr32.dll");
|
HMODULE module = LoadLibraryA("cfgmgr32.dll");
|
||||||
if (module) {
|
if (module) {
|
||||||
_CM_Get_DevNode_PropertyW = (pCM_Get_DevNode_Property)GetProcAddress(module, "CM_Get_DevNode_PropertyW");
|
_CM_Get_DevNode_PropertyW = (pCM_Get_DevNode_Property)GetProcAddress(module, "CM_Get_DevNode_PropertyW");
|
||||||
@ -45,83 +61,16 @@ WinInputDeviceManager() :
|
|||||||
_CM_Get_DevNode_PropertyW = nullptr;
|
_CM_Get_DevNode_PropertyW = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now create a message-only window for the raw input.
|
// If we have threading enabled, start a thread with a message-only window
|
||||||
WNDCLASSEX wc = {};
|
// loop to listen for input events.
|
||||||
wc.cbSize = sizeof(WNDCLASSEX);
|
#ifdef HAVE_THREADS
|
||||||
wc.lpfnWndProc = window_proc;
|
if (Thread::is_threading_supported()) {
|
||||||
wc.hInstance = GetModuleHandle(nullptr);
|
PT(Thread) thread = new InputThread(this);
|
||||||
wc.lpszClassName = "InputDeviceManager";
|
thread->start(TP_normal, false);
|
||||||
if (!RegisterClassEx(&wc)) {
|
} else
|
||||||
device_cat.warning()
|
#endif
|
||||||
<< "Failed to register message-only window class.\n";
|
{
|
||||||
return;
|
setup_message_loop();
|
||||||
}
|
|
||||||
|
|
||||||
_message_hwnd = CreateWindowEx(0, wc.lpszClassName, "InputDeviceManager", 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr);
|
|
||||||
if (!_message_hwnd) {
|
|
||||||
device_cat.warning()
|
|
||||||
<< "Failed to create message-only window.\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now listen for raw input devices using the created message loop.
|
|
||||||
RAWINPUTDEVICE rid[3];
|
|
||||||
rid[0].usUsagePage = 1;
|
|
||||||
rid[0].usUsage = 4; // Joysticks
|
|
||||||
rid[0].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
|
|
||||||
rid[0].hwndTarget = _message_hwnd;
|
|
||||||
rid[1].usUsagePage = 1;
|
|
||||||
rid[1].usUsage = 5; // Gamepads
|
|
||||||
rid[1].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
|
|
||||||
rid[1].hwndTarget = _message_hwnd;
|
|
||||||
rid[2].usUsagePage = 1;
|
|
||||||
rid[2].usUsage = 8; // Multi-axis controllers (including 3D mice)
|
|
||||||
rid[2].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
|
|
||||||
rid[2].hwndTarget = _message_hwnd;
|
|
||||||
if (!RegisterRawInputDevices(rid, 3, sizeof(RAWINPUTDEVICE))) {
|
|
||||||
device_cat.warning()
|
|
||||||
<< "Failed to register raw input devices.\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do we have any XInput devices plugged in now?
|
|
||||||
int num_xinput = 0;
|
|
||||||
HANDLE xinput_handle;
|
|
||||||
RAWINPUTDEVICELIST devices[64];
|
|
||||||
UINT num_devices = 64;
|
|
||||||
num_devices = GetRawInputDeviceList(devices, &num_devices, sizeof(RAWINPUTDEVICELIST));
|
|
||||||
if (num_devices == (UINT)-1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (UINT i = 0; i < num_devices; ++i) {
|
|
||||||
if (devices[i].dwType != RIM_TYPEHID) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
HANDLE handle = devices[i].hDevice;
|
|
||||||
UINT size;
|
|
||||||
if (GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, nullptr, &size) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *path = (char *)alloca(size);
|
|
||||||
if (path == nullptr ||
|
|
||||||
GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, (void *)path, &size) < 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strstr(path, "&IG_") != nullptr) {
|
|
||||||
xinput_handle = handle;
|
|
||||||
++num_xinput;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (num_xinput == 1) {
|
|
||||||
// There's only one XInput device, so we know which one it is.
|
|
||||||
on_input_device_arrival(xinput_handle);
|
|
||||||
} else if (num_xinput > 0) {
|
|
||||||
// Just poll all the XInput devices.
|
|
||||||
_xinput_device0.detect(this);
|
|
||||||
_xinput_device1.detect(this);
|
|
||||||
_xinput_device2.detect(this);
|
|
||||||
_xinput_device3.detect(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,8 +80,17 @@ WinInputDeviceManager() :
|
|||||||
WinInputDeviceManager::
|
WinInputDeviceManager::
|
||||||
~WinInputDeviceManager() {
|
~WinInputDeviceManager() {
|
||||||
if (_message_hwnd != nullptr) {
|
if (_message_hwnd != nullptr) {
|
||||||
DestroyWindow(_message_hwnd);
|
#ifdef HAVE_THREADS
|
||||||
_message_hwnd = nullptr;
|
if (Thread::is_threading_supported()) {
|
||||||
|
HWND hwnd = _message_hwnd;
|
||||||
|
if (hwnd) {
|
||||||
|
SendMessage(hwnd, WM_QUIT, 0, 0);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
destroy_message_loop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,10 +355,110 @@ on_input_device_removal(HANDLE handle) {
|
|||||||
*/
|
*/
|
||||||
void WinInputDeviceManager::
|
void WinInputDeviceManager::
|
||||||
update() {
|
update() {
|
||||||
MSG msg;
|
}
|
||||||
while (PeekMessage(&msg, _message_hwnd, WM_INPUT_DEVICE_CHANGE, WM_INPUT, PM_REMOVE)) {
|
|
||||||
TranslateMessage(&msg);
|
/**
|
||||||
DispatchMessage(&msg);
|
* Sets up a Windows message loop. Should be called from the thread that will
|
||||||
|
* be handling the messages.
|
||||||
|
*/
|
||||||
|
HWND WinInputDeviceManager::
|
||||||
|
setup_message_loop() {
|
||||||
|
_message_hwnd = 0;
|
||||||
|
|
||||||
|
// Now create a message-only window for the raw input.
|
||||||
|
WNDCLASSEX wc = {};
|
||||||
|
wc.cbSize = sizeof(WNDCLASSEX);
|
||||||
|
wc.lpfnWndProc = window_proc;
|
||||||
|
wc.hInstance = GetModuleHandle(nullptr);
|
||||||
|
wc.lpszClassName = "InputDeviceManager";
|
||||||
|
if (!RegisterClassEx(&wc)) {
|
||||||
|
device_cat.warning()
|
||||||
|
<< "Failed to register message-only window class for input device detection.\n";
|
||||||
|
} else {
|
||||||
|
_message_hwnd = CreateWindowEx(0, wc.lpszClassName, "InputDeviceManager", 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr);
|
||||||
|
if (!_message_hwnd) {
|
||||||
|
device_cat.warning()
|
||||||
|
<< "Failed to create message-only window for input device detection.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now listen for raw input devices using the created message loop.
|
||||||
|
RAWINPUTDEVICE rid[3];
|
||||||
|
rid[0].usUsagePage = 1;
|
||||||
|
rid[0].usUsage = 4; // Joysticks
|
||||||
|
rid[0].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
|
||||||
|
rid[0].hwndTarget = _message_hwnd;
|
||||||
|
rid[1].usUsagePage = 1;
|
||||||
|
rid[1].usUsage = 5; // Gamepads
|
||||||
|
rid[1].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
|
||||||
|
rid[1].hwndTarget = _message_hwnd;
|
||||||
|
rid[2].usUsagePage = 1;
|
||||||
|
rid[2].usUsage = 8; // Multi-axis controllers (including 3D mice)
|
||||||
|
rid[2].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
|
||||||
|
rid[2].hwndTarget = _message_hwnd;
|
||||||
|
if (!RegisterRawInputDevices(rid, 3, sizeof(RAWINPUTDEVICE))) {
|
||||||
|
device_cat.warning()
|
||||||
|
<< "Failed to register raw input devices.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have any XInput devices plugged in now?
|
||||||
|
int num_xinput = 0;
|
||||||
|
HANDLE xinput_handle;
|
||||||
|
RAWINPUTDEVICELIST devices[64];
|
||||||
|
UINT num_devices = 64;
|
||||||
|
num_devices = GetRawInputDeviceList(devices, &num_devices, sizeof(RAWINPUTDEVICELIST));
|
||||||
|
if (num_devices == (UINT)-1) {
|
||||||
|
num_devices = 0;
|
||||||
|
}
|
||||||
|
for (UINT i = 0; i < num_devices; ++i) {
|
||||||
|
if (devices[i].dwType != RIM_TYPEHID) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
HANDLE handle = devices[i].hDevice;
|
||||||
|
UINT size;
|
||||||
|
if (GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, nullptr, &size) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *path = (char *)alloca(size);
|
||||||
|
if (path == nullptr ||
|
||||||
|
GetRawInputDeviceInfoA(handle, RIDI_DEVICENAME, (void *)path, &size) < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strstr(path, "&IG_") != nullptr) {
|
||||||
|
xinput_handle = handle;
|
||||||
|
++num_xinput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (num_xinput == 1) {
|
||||||
|
// There's only one XInput device, so we know which one it is.
|
||||||
|
on_input_device_arrival(xinput_handle);
|
||||||
|
} else if (num_xinput > 0) {
|
||||||
|
// Just poll all the XInput devices.
|
||||||
|
_xinput_device0.detect(this);
|
||||||
|
_xinput_device1.detect(this);
|
||||||
|
_xinput_device2.detect(this);
|
||||||
|
_xinput_device3.detect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _message_hwnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tears down the message loop. Should be called from the thread that called
|
||||||
|
* setup_message_loop().
|
||||||
|
*/
|
||||||
|
void WinInputDeviceManager::
|
||||||
|
destroy_message_loop() {
|
||||||
|
HWND hwnd = nullptr;
|
||||||
|
{
|
||||||
|
LightMutexHolder holder(_lock);
|
||||||
|
std::swap(_message_hwnd, hwnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwnd) {
|
||||||
|
DestroyWindow(hwnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,4 +500,36 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
|||||||
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_THREADS
|
||||||
|
/**
|
||||||
|
* Thread entry point for the input listener thread.
|
||||||
|
*/
|
||||||
|
void InputThread::
|
||||||
|
thread_main() {
|
||||||
|
WinInputDeviceManager *manager = _manager;
|
||||||
|
HWND hwnd = manager->setup_message_loop();
|
||||||
|
if (!hwnd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_cat.is_debug()) {
|
||||||
|
device_cat.debug()
|
||||||
|
<< "Started input device listener thread.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
MSG msg;
|
||||||
|
while (GetMessage(&msg, nullptr, 0, 0) > 0) {
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessage(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_cat.is_debug()) {
|
||||||
|
device_cat.debug()
|
||||||
|
<< "Stopping input device listener thread.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
manager->destroy_message_loop();
|
||||||
|
}
|
||||||
|
#endif // HAVE_THREADS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -41,6 +41,9 @@ public:
|
|||||||
void on_input_device_arrival(HANDLE handle);
|
void on_input_device_arrival(HANDLE handle);
|
||||||
void on_input_device_removal(HANDLE handle);
|
void on_input_device_removal(HANDLE handle);
|
||||||
|
|
||||||
|
HWND setup_message_loop();
|
||||||
|
void destroy_message_loop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// There are always exactly four of these in existence.
|
// There are always exactly four of these in existence.
|
||||||
XInputDevice _xinput_device0;
|
XInputDevice _xinput_device0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user