feat: update
This commit is contained in:
parent
73bafa3e86
commit
9de53f0f5b
|
@ -0,0 +1,63 @@
|
|||
cmake_minimum_required (VERSION 3.15)
|
||||
|
||||
project (serialport)
|
||||
|
||||
set (ADMAKE_DISABLE_ADDRESS_SANITIZER ON)
|
||||
|
||||
include (CMakeListsPub)
|
||||
|
||||
set (TARGET_NAME ${CMAKE_PROJECT_NAME})
|
||||
|
||||
file (GLOB_RECURSE SRC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
|
||||
|
||||
add_library (${TARGET_NAME}_OBJ OBJECT ${SRC})
|
||||
|
||||
if (APPLE)
|
||||
|
||||
elseif (ANDROID)
|
||||
add_compile_options (-ffunction-sections -fdata-sections -fvisibility=hidden -Wl,--gc-sections)
|
||||
elseif (UNIX)
|
||||
|
||||
elseif (WIN32)
|
||||
# Disable the lib export by DLL
|
||||
set (CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF)
|
||||
endif ()
|
||||
|
||||
target_include_directories (${TARGET_NAME}_OBJ
|
||||
PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/source")
|
||||
|
||||
add_library (${TARGET_NAME} STATIC $<TARGET_OBJECTS:${TARGET_NAME}_OBJ>)
|
||||
add_library (${TARGET_NAME}_SO SHARED $<TARGET_OBJECTS:${TARGET_NAME}_OBJ>)
|
||||
|
||||
if (WIN32)
|
||||
set_target_properties (${TARGET_NAME}_SO PROPERTIES
|
||||
OUTPUT_NAME "serialportdll")
|
||||
target_link_libraries (${TARGET_NAME}_SO PUBLIC user32)
|
||||
elseif (APPLE)
|
||||
set (DEP_FRAMEWOKS Foundation)
|
||||
|
||||
macro (add_osx_framework target fwname)
|
||||
find_library (FRAMEWORK_${fwname}
|
||||
NAMES ${fwname}
|
||||
PATHS ${CMAKE_OSX_SYSROOT}/System/Library
|
||||
PATH_SUFFIXES Frameworks
|
||||
NO_DEFAULT_PATH)
|
||||
if (${FRAMEWORK_${fwname}} STREQUAL FRAMEWORK_${fwname}-NOTFOUND)
|
||||
message (ERROR ": Framework ${fwname} not found")
|
||||
else ()
|
||||
message (STATUS "Framework ${fwname} found at ${FRAMEWORK_${fwname}}")
|
||||
target_link_libraries (${target} "-framework ${fwname}")
|
||||
endif ()
|
||||
endmacro ()
|
||||
|
||||
foreach (framework ${DEP_FRAMEWOKS})
|
||||
add_osx_framework (${TARGET_NAME}_SO ${framework})
|
||||
endforeach ()
|
||||
|
||||
endif ()
|
||||
|
||||
if (DEFINED ADMAKE_BUILD_TEST)
|
||||
endif ()
|
|
@ -0,0 +1,3 @@
|
|||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
|
||||
#endif /* OS_LINUX || OS_ANDROID */
|
|
@ -0,0 +1,182 @@
|
|||
#if defined(OS_MACOS)
|
||||
|
||||
// macOS
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/serial/IOSerialKeys.h>
|
||||
|
||||
#include <memory> /**< std::shared_ptr */
|
||||
#include <thread> /**< std::thread */
|
||||
|
||||
class SerialPortHotPlugDelegate {
|
||||
public:
|
||||
virtual void OnConnected() = 0;
|
||||
virtual void OnDisconnected() = 0;
|
||||
};
|
||||
|
||||
class SerialPortHotPlugMonitor {
|
||||
SerialPortHotPlugMonitor(std::shared_ptr<SerialPortHotPlugDelegate> delegate)
|
||||
: notification_port_(nullptr), serial_port_add_iter_(0),
|
||||
serial_port_remove_iter_(0), delegate_(delegate) {
|
||||
Init();
|
||||
}
|
||||
|
||||
~SerialPortHotPlugMonitor() {
|
||||
if (thread_) {
|
||||
if (thread_.joinable()) {
|
||||
thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
if (notification_port_ != nullptr) {
|
||||
IONotificationPortDestroy(notification_port_);
|
||||
}
|
||||
|
||||
if (serial_port_add_iter_) {
|
||||
IOObjectRelease(serial_port_add_iter_);
|
||||
}
|
||||
|
||||
if (serial_port_remove_iter_) {
|
||||
IOObjectRelease(serial_port_remove_iter_);
|
||||
}
|
||||
}
|
||||
|
||||
int connectHotPlugEvent(itas109::CSerialPortHotPlugListener *event) {
|
||||
if (event) {
|
||||
p_listener = event;
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int disconnectHotPlugEvent() {
|
||||
p_listener = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
inline void operator()() {
|
||||
// create notificaiton
|
||||
notification_port_ = IONotificationPortCreate(kIOMasterPortDefault);
|
||||
if (!notification_port_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create run loop
|
||||
CFRunLoopSourceRef runLoopSource =
|
||||
IONotificationPortGetRunLoopSource(notification_port_);
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,
|
||||
kCFRunLoopDefaultMode);
|
||||
|
||||
kern_return_t result;
|
||||
|
||||
// match add serial
|
||||
CFMutableDictionaryRef matching_add_dict =
|
||||
IOServiceMatching(kIOSerialBSDServiceValue);
|
||||
if (!matching_add_dict) {
|
||||
IONotificationPortDestroy(notification_port_);
|
||||
return;
|
||||
}
|
||||
|
||||
// register serial add event
|
||||
result = IOServiceAddMatchingNotification(
|
||||
notification_port_, kIOPublishNotification, matching_add_dict,
|
||||
SerialPortAdd, this, &serial_port_add_iter_);
|
||||
if (result != KERN_SUCCESS) {
|
||||
// fprintf(stderr, "Failed to add publish notification: %d.\n", result);
|
||||
CFRelease(matching_add_dict);
|
||||
IONotificationPortDestroy(notification_port_);
|
||||
return;
|
||||
}
|
||||
// deal current device
|
||||
SerialPortAdd(this, serial_port_add_iter_);
|
||||
// clear dirct
|
||||
// CFRelease(matchingAddDict); // TODO
|
||||
|
||||
// match remove serial
|
||||
CFMutableDictionaryRef matching_remove_dict =
|
||||
IOServiceMatching(kIOSerialBSDServiceValue);
|
||||
if (!matching_remove_dict) {
|
||||
// fprintf(stderr, "Failed to create matching add dictionary.\n");
|
||||
IONotificationPortDestroy(notification_port_);
|
||||
return;
|
||||
}
|
||||
// register serial remove event
|
||||
result = IOServiceAddMatchingNotification(
|
||||
notification_port_, kIOTerminatedNotification, matching_remove_dict,
|
||||
SerialPortRemove, this, &serial_port_remove_iter_);
|
||||
if (result != KERN_SUCCESS) {
|
||||
// fprintf(stderr, "Failed to add terminated notification: %d.\n",
|
||||
// result);
|
||||
CFRelease(matching_remove_dict);
|
||||
IONotificationPortDestroy(notification_port_);
|
||||
return;
|
||||
}
|
||||
// deal current device
|
||||
SerialPortRemove(this, serial_port_remove_iter_);
|
||||
// clear dict
|
||||
// CFRelease(matchingRemoveDict); // TODO
|
||||
|
||||
// message loop (could not delete)
|
||||
CFRunLoopRun();
|
||||
}
|
||||
|
||||
private:
|
||||
bool Init() {
|
||||
thread_ = std::thread(&SerialPortHotPlugMonitor::operator(), this);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static void SerialPortAdd(void *refcon, io_iterator_t iterator) {
|
||||
SerialPortHotPlugMonitor *monitor = reinterpret_cast<SerialPortHotPlugMonitor *>()refcon);
|
||||
if (monitor) {
|
||||
io_object_t serial_port;
|
||||
while ((serial_port = IOIteratorNext(iterator))) {
|
||||
char port_name[256];
|
||||
CFStringRef cf_string = (CFStringRef)IORegistryEntryCreateCFProperty(
|
||||
serial_port, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
|
||||
if (cf_string != nullptr) {
|
||||
CFStringGetCString(cf_string, port_name, sizeof(port_name),
|
||||
kCFStringEncodingUTF8);
|
||||
CFRelease(cf_string);
|
||||
if (monitor->delegate_) {
|
||||
monitor->delegate_->OnConnected(port_name, 1);
|
||||
}
|
||||
}
|
||||
IOObjectRelease(serial_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SerialPortRemove(void *refcon, io_iterator_t iterator) {
|
||||
SerialPortHotPlugMonitor *monitor = reinterpret_cast<SerialPortHotPlugMonitor *>()refcon);
|
||||
if (monitor) {
|
||||
io_object_t serial_port;
|
||||
while ((serial_port = IOIteratorNext(iterator))) {
|
||||
char port_name[256];
|
||||
CFStringRef cf_string = (CFStringRef)IORegistryEntryCreateCFProperty(
|
||||
serial_port, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
|
||||
if (cf_string != nullptr) {
|
||||
CFStringGetCString(cf_string, port_name, sizeof(port_name),
|
||||
kCFStringEncodingUTF8);
|
||||
CFRelease(cf_string);
|
||||
if (monitor->delegate_) {
|
||||
monitor->delegate_->OnDisconnected(port_name, 0);
|
||||
}
|
||||
}
|
||||
IOObjectRelease(serial_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
IONotificationPortRef notification_port_;
|
||||
io_iterator_t serial_port_add_iter_;
|
||||
io_iterator_t serial_port_remove_iter_;
|
||||
|
||||
std::thread thread_;
|
||||
std::shared_ptr<SerialPortHotPlugDelegate> delegate_;
|
||||
};
|
||||
#endif /* OS_MACOS */
|
|
@ -0,0 +1,156 @@
|
|||
#if defined(OS_WINDOWS)
|
||||
|
||||
// CreateWindowEx GetMessage DispatchMessage RegisterClassEx UnregisterClass
|
||||
// DefWindowProc SetWindowLong GetWindowLong
|
||||
#include <dbt.h>
|
||||
#include <tchar.h> /**< _T */
|
||||
#include <windows.h>
|
||||
|
||||
#include <memory> /**< std::shared_ptr */
|
||||
#include <thread> /**< std::thread */
|
||||
|
||||
#define CLASS_NAME _T("SerialPortHotPlugMonitorWnd")
|
||||
|
||||
class SerialPortHotPlugDelegate {
|
||||
public:
|
||||
virtual void OnConnected() = 0;
|
||||
virtual void OnDisconnected() = 0;
|
||||
};
|
||||
|
||||
class SerialPortHotPlugMonitor {
|
||||
SerialPortHotPlugMonitor(std::shared_ptr<SerialPortHotPlugDelegate> delegate)
|
||||
: delegate(delegate_) {
|
||||
Init();
|
||||
}
|
||||
|
||||
~SerialPortHotPlugMonitor() {
|
||||
if (thread_) {
|
||||
if (thread_.joinable()) {
|
||||
thread_.join();
|
||||
}
|
||||
}
|
||||
UnregisterClass(CLASS_NAME, GetModuleHandle(NULL));
|
||||
}
|
||||
|
||||
private:
|
||||
inline void operator()() {
|
||||
// create hidden window to receive device change messages
|
||||
if (nullptr == CreateWindowEx(0, CLASS_NAME, 0, 0, 0, 0, 0, 0,
|
||||
0 /*HWND_MESSAGE*/, 0,
|
||||
GetModuleHandle(nullptr), this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// message loop
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, nullptr, 0, 0)) {
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool Init() {
|
||||
// https://docs.microsoft.com/zh-cn/windows/win32/devio/registering-for-device-notification
|
||||
WNDCLASSEX wnd_cls{0};
|
||||
|
||||
HINSTANCE instance = GetModuleHandle(nullptr);
|
||||
|
||||
if (nullptr == instance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wnd_cls.cbSize = sizeof(WNDCLASSEX);
|
||||
wnd_cls.lpszClassName = CLASS_NAME;
|
||||
wnd_cls.lpfnWndProc = reinterpret_cast<WNDPROC>(WinProc);
|
||||
wnd_cls.hInstance = instance;
|
||||
|
||||
if (!RegisterClassEx(&wnd_cls)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
thread_ = std::thread(&SerialPortHotPlugMonitor::operator(), this);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam,
|
||||
LPARAM lParam) {
|
||||
switch (message) {
|
||||
case WM_CREATE:
|
||||
SetWindowLongPtr(hWnd, GWLP_USERDATA,
|
||||
(LONG_PTR)((CREATESTRUCT *)lParam)->lpCreateParams);
|
||||
break;
|
||||
case WM_DEVICECHANGE: {
|
||||
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
|
||||
|
||||
if (lpdb && DBT_DEVTYP_PORT == lpdb->dbch_devicetype) {
|
||||
SerialPortHotPlugMonitor *monitor =
|
||||
(SerialPortHotPlugMonitor *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
|
||||
if (base && base->delegate) {
|
||||
PDEV_BROADCAST_PORT devinfo = (PDEV_BROADCAST_PORT)lpdb;
|
||||
|
||||
if (DBT_DEVICEARRIVAL == wParam) {
|
||||
#ifdef UNICODE
|
||||
char port_name[256];
|
||||
#ifdef CSERIALPORT_USE_UTF8
|
||||
monirot->delegate_->OnConnected(
|
||||
WCharToUTF8(port_name, 256, devinfo->dbcp_name), 1);
|
||||
#else
|
||||
monitor->delegate_->OnConnected(
|
||||
WCharToNativeMB(port_name, 256, devinfo->dbcp_name), 1);
|
||||
#endif
|
||||
#else
|
||||
#ifdef CSERIALPORT_USE_UTF8
|
||||
char portNameUTF8[256];
|
||||
wchar_t portNameWChar[256];
|
||||
// ANSI to WChar
|
||||
NativeMBToWChar(portNameWChar, 256, devinfo->dbcp_name);
|
||||
// WChar to UTF8
|
||||
monitor->delegate_->OnConnected(
|
||||
WCharToUTF8(portNameUTF8, 256, portNameWChar), 1);
|
||||
#else
|
||||
monitor->delegate_->OnConnected(devinfo->dbcp_name, 1);
|
||||
#endif
|
||||
#endif
|
||||
} else if (DBT_DEVICEREMOVECOMPLETE == wParam) {
|
||||
#ifdef UNICODE
|
||||
char portName[256];
|
||||
#ifdef CSERIALPORT_USE_UTF8
|
||||
monitor->delegate_->OnConnected(
|
||||
WCharToUTF8(portName, 256, devinfo->dbcp_name), 0);
|
||||
#else
|
||||
monitor->delegate_->OnConnected(
|
||||
WCharToNativeMB(portName, 256, devinfo->dbcp_name), 0);
|
||||
#endif
|
||||
#else
|
||||
#ifdef CSERIALPORT_USE_UTF8
|
||||
char portNameUTF8[256];
|
||||
wchar_t portNameWChar[256];
|
||||
// ANSI to WChar
|
||||
NativeMBToWChar(portNameWChar, 256, pDevInf->dbcp_name);
|
||||
// WChar to UTF8
|
||||
monitor->delegate_->OnConnected(
|
||||
WCharToUTF8(portNameUTF8, 256, portNameWChar), 0);
|
||||
#else
|
||||
monitor->delegate_->OnConnected(devinfo->dbcp_name, 0);
|
||||
#endif
|
||||
#endif
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// send all other messages on to the default windows handler
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
private:
|
||||
std::thread thread_;
|
||||
std::shared_ptr<SerialPortHotPlugDelegate> delegate_;
|
||||
};
|
||||
|
||||
#endif /* OS_WINDOWS */
|
Loading…
Reference in New Issue