Compare commits
No commits in common. "7e008c154b50a9ed80b2e4606f444dd6519234cc" and "02da76e9583030d0b5742687b16a77641b232cbe" have entirely different histories.
7e008c154b
...
02da76e958
@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required (VERSION 3.12)
|
cmake_minimum_required (VERSION 3.10)
|
||||||
project (usb-drivers VERSION 1.1.0
|
project (usb-drivers VERSION 1.0.0
|
||||||
DESCRIPTION "User space USB drivers" LANGUAGES C)
|
DESCRIPTION "User space USB drivers" LANGUAGES C)
|
||||||
|
|
||||||
# Moar warnings
|
# Moar warnings
|
||||||
@ -35,62 +35,9 @@ endif ()
|
|||||||
# Dependencies
|
# Dependencies
|
||||||
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/liberty/cmake)
|
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/liberty/cmake)
|
||||||
|
|
||||||
# TODO(p): Shove this into IconUtils.cmake.
|
|
||||||
function (icon_to_iconset_size name svg size iconset outputs)
|
|
||||||
math (EXPR _size2x "${size} * 2")
|
|
||||||
set (_dimensions "${size}x${size}")
|
|
||||||
set (_png1x "${iconset}/icon_${_dimensions}.png")
|
|
||||||
set (_png2x "${iconset}/icon_${_dimensions}@2x.png")
|
|
||||||
set (${outputs} "${_png1x};${_png2x}" PARENT_SCOPE)
|
|
||||||
|
|
||||||
set (_find_program_REQUIRE)
|
|
||||||
if (NOT ${CMAKE_VERSION} VERSION_LESS 3.18.0)
|
|
||||||
set (_find_program_REQUIRE REQUIRED)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
find_program (rsvg_convert_EXECUTABLE rsvg-convert ${_find_program_REQUIRE})
|
|
||||||
add_custom_command (OUTPUT "${_png1x}" "${_png2x}"
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${iconset}"
|
|
||||||
COMMAND ${rsvg_convert_EXECUTABLE} "--output=${_png1x}"
|
|
||||||
"--width=${size}" "--height=${size}" -- "${svg}"
|
|
||||||
COMMAND ${rsvg_convert_EXECUTABLE} "--output=${_png2x}"
|
|
||||||
"--width=${_size2x}" "--height=${_size2x}" -- "${svg}"
|
|
||||||
DEPENDS "${svg}"
|
|
||||||
COMMENT "Generating ${name} ${_dimensions} icons" VERBATIM)
|
|
||||||
endfunction ()
|
|
||||||
function (icon_to_icns svg output_basename output)
|
|
||||||
get_filename_component (_name "${output_basename}" NAME_WE)
|
|
||||||
set (_iconset "${PROJECT_BINARY_DIR}/${_name}.iconset")
|
|
||||||
set (_icon "${PROJECT_BINARY_DIR}/${output_basename}")
|
|
||||||
set (${output} "${_icon}" PARENT_SCOPE)
|
|
||||||
|
|
||||||
set (_icon_png_list)
|
|
||||||
foreach (_icon_size 16 32 128 256 512)
|
|
||||||
icon_to_iconset_size ("${_name}" "${svg}"
|
|
||||||
"${_icon_size}" "${_iconset}" _icon_pngs)
|
|
||||||
list (APPEND _icon_png_list ${_icon_pngs})
|
|
||||||
endforeach ()
|
|
||||||
add_custom_command (OUTPUT "${_icon}"
|
|
||||||
COMMAND iconutil -c icns -o "${_icon}" "${_iconset}"
|
|
||||||
DEPENDS ${_icon_png_list}
|
|
||||||
COMMENT "Generating ${_name} icon" VERBATIM)
|
|
||||||
set_source_files_properties ("${_icon}" PROPERTIES
|
|
||||||
MACOSX_PACKAGE_LOCATION Resources)
|
|
||||||
endfunction ()
|
|
||||||
|
|
||||||
find_package (PkgConfig REQUIRED)
|
find_package (PkgConfig REQUIRED)
|
||||||
pkg_check_modules (libusb libusb-1.0)
|
pkg_check_modules (libusb libusb-1.0)
|
||||||
|
pkg_search_module (hidapi hidapi hidapi-hidraw hidapi-libusb)
|
||||||
# On MSYS2, the CMake package cannot link statically, but pkg-config can.
|
|
||||||
# On macOS, we explicitly want to use the CMake package.
|
|
||||||
if (WIN32)
|
|
||||||
pkg_search_module (hidapi hidapi hidapi-hidraw hidapi-libusb)
|
|
||||||
else ()
|
|
||||||
find_package (hidapi)
|
|
||||||
set (hidapi_INCLUDE_DIRS)
|
|
||||||
set (hidapi_LIBRARY_DIRS)
|
|
||||||
set (hidapi_LIBRARIES hidapi::hidapi)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
option (WITH_LIBUSB "Compile with libusb-based utilities" ${libusb_FOUND})
|
option (WITH_LIBUSB "Compile with libusb-based utilities" ${libusb_FOUND})
|
||||||
option (WITH_HIDAPI "Compile with hidapi-based utilities" ${hidapi_FOUND})
|
option (WITH_HIDAPI "Compile with hidapi-based utilities" ${hidapi_FOUND})
|
||||||
@ -150,6 +97,7 @@ if (WITH_HIDAPI AND WIN32)
|
|||||||
|
|
||||||
set (icon_ico ${PROJECT_BINARY_DIR}/eizoctltray.ico)
|
set (icon_ico ${PROJECT_BINARY_DIR}/eizoctltray.ico)
|
||||||
icon_for_win32 (${icon_ico} "${icon_png_list}" "${icon_png}")
|
icon_for_win32 (${icon_ico} "${icon_png_list}" "${icon_png}")
|
||||||
|
list (APPEND icon_ico_list )
|
||||||
set_property (SOURCE eizoctltray.rc
|
set_property (SOURCE eizoctltray.rc
|
||||||
APPEND PROPERTY OBJECT_DEPENDS ${icon_ico})
|
APPEND PROPERTY OBJECT_DEPENDS ${icon_ico})
|
||||||
|
|
||||||
@ -160,27 +108,9 @@ if (WITH_HIDAPI AND WIN32)
|
|||||||
target_link_directories (eizoctltray PUBLIC ${hidapi_LIBRARY_DIRS})
|
target_link_directories (eizoctltray PUBLIC ${hidapi_LIBRARY_DIRS})
|
||||||
target_link_libraries (eizoctltray ${hidapi_LIBRARIES} powrprof)
|
target_link_libraries (eizoctltray ${hidapi_LIBRARIES} powrprof)
|
||||||
endif ()
|
endif ()
|
||||||
if (WITH_HIDAPI AND APPLE)
|
|
||||||
list (APPEND targets_gui eizoctltray)
|
|
||||||
|
|
||||||
# We override the language for the command line target as well,
|
|
||||||
# but that doesn't and must not pose any problems.
|
|
||||||
enable_language (OBJC)
|
|
||||||
set_source_files_properties (eizoctl.c PROPERTIES LANGUAGE OBJC)
|
|
||||||
|
|
||||||
set (MACOSX_BUNDLE_GUI_IDENTIFIER name.janouch.eizoctltray)
|
|
||||||
set (MACOSX_BUNDLE_ICON_FILE eizoctltray.icns)
|
|
||||||
icon_to_icns (${PROJECT_SOURCE_DIR}/eizoctltray.svg
|
|
||||||
"${MACOSX_BUNDLE_ICON_FILE}" icon)
|
|
||||||
|
|
||||||
add_executable (eizoctltray MACOSX_BUNDLE eizoctl.c "${icon}")
|
|
||||||
target_compile_definitions (eizoctltray PUBLIC -DTRAY)
|
|
||||||
target_compile_options (eizoctltray PUBLIC -fobjc-arc)
|
|
||||||
target_link_libraries (eizoctltray ${hidapi_LIBRARIES} "-framework Cocoa")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Generate documentation from help output
|
# Generate documentation from help output
|
||||||
if (NOT WIN32 AND NOT CMAKE_CROSSCOMPILING)
|
if (NOT CMAKE_CROSSCOMPILING)
|
||||||
find_program (HELP2MAN_EXECUTABLE help2man)
|
find_program (HELP2MAN_EXECUTABLE help2man)
|
||||||
if (NOT HELP2MAN_EXECUTABLE)
|
if (NOT HELP2MAN_EXECUTABLE)
|
||||||
message (FATAL_ERROR "help2man not found")
|
message (FATAL_ERROR "help2man not found")
|
||||||
|
9
NEWS
9
NEWS
@ -1,9 +0,0 @@
|
|||||||
1.1.0 (2024-11-28)
|
|
||||||
|
|
||||||
* Ported eizoctltray to macOS as well
|
|
||||||
|
|
||||||
|
|
||||||
1.0.0 (2024-11-26)
|
|
||||||
|
|
||||||
* Initial release
|
|
||||||
|
|
28
README.adoc
28
README.adoc
@ -19,12 +19,11 @@ and may not run at the same time, as it would contend for device access.
|
|||||||
|
|
||||||
eizoctltray
|
eizoctltray
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
_eizoctltray_ is a derived Windows/macOS utility that can stay in the systray.
|
_eizoctltray_ is a derived Windows utility that can stay in the systray.
|
||||||
When holding the Shift or Control keys while switching signal inputs,
|
When holding the Shift or Control keys while switching signal inputs,
|
||||||
it will also suspend or power off the system, respectively.
|
it will also suspend or power off the system, respectively.
|
||||||
|
|
||||||
image:eizoctltray-win.png["eizoctltray on Windows with expanded menu", 343, 278]
|
image::eizoctltray.png["eizoctltray with expanded context menu", 343, 229]
|
||||||
image:eizoctltray-mac.png["eizoctltray on macOS with expanded menu", 343, 278]
|
|
||||||
|
|
||||||
elksmart-comm
|
elksmart-comm
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
@ -78,29 +77,6 @@ Or you can try telling CMake to make a package for you. For Debian it is:
|
|||||||
$ cpack -G DEB
|
$ cpack -G DEB
|
||||||
# dpkg -i usb-drivers-*.deb
|
# dpkg -i usb-drivers-*.deb
|
||||||
|
|
||||||
Windows
|
|
||||||
~~~~~~~
|
|
||||||
You can either build within an MSYS2 environment,
|
|
||||||
or cross-compile using Mingw-w64:
|
|
||||||
|
|
||||||
$ sh -e cmake/Win64Depends.sh
|
|
||||||
$ cmake -DCMAKE_TOOLCHAIN_FILE=liberty/cmake/toolchains/MinGW-w64-x64.cmake \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release -B build
|
|
||||||
$ cmake --build build
|
|
||||||
|
|
||||||
macOS
|
|
||||||
~~~~~
|
|
||||||
You can either build _eizoctltray_ against Homebrew,
|
|
||||||
or link hidapi statically for a standalone portable app:
|
|
||||||
|
|
||||||
$ git clone https://github.com/libusb/hidapi.git
|
|
||||||
$ cmake -S hidapi -DBUILD_SHARED_LIBS=OFF \
|
|
||||||
-DCMAKE_INSTALL_PREFIX=$PWD/hidapi-build \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release -B hidapi-build
|
|
||||||
$ cmake --build hidapi-build -- install
|
|
||||||
$ cmake -Dhidapi_ROOT=$PWD/hidapi-build -DCMAKE_BUILD_TYPE=Release -B build
|
|
||||||
$ cmake --build build
|
|
||||||
|
|
||||||
Contributing and Support
|
Contributing and Support
|
||||||
------------------------
|
------------------------
|
||||||
Use https://git.janouch.name/p/usb-drivers to report bugs, request features,
|
Use https://git.janouch.name/p/usb-drivers to report bugs, request features,
|
||||||
|
305
eizoctl.c
305
eizoctl.c
@ -880,20 +880,6 @@ eizo_get_input_port(struct eizo_monitor *m, uint16_t *port)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
eizo_get_input_ports(struct eizo_monitor *m, uint16_t *ports, size_t size)
|
|
||||||
{
|
|
||||||
struct eizo_profile_item *item = &m->profile[EIZO_PROFILE_KEY_INPUT_PORTS];
|
|
||||||
if (item->len) {
|
|
||||||
for (size_t i = 0; i < size && i < item->len / 4; i++)
|
|
||||||
ports[i] = peek_u16le(item->data + i * 4);
|
|
||||||
} else {
|
|
||||||
const uint16_t *db = eizo_ports_by_product_name(m->product);
|
|
||||||
for (size_t i = 0; i < size && db && db[i]; i++)
|
|
||||||
ports[i] = db[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t
|
static uint16_t
|
||||||
eizo_resolve_port(struct eizo_monitor *m, const char *port)
|
eizo_resolve_port(struct eizo_monitor *m, const char *port)
|
||||||
{
|
{
|
||||||
@ -1128,7 +1114,7 @@ main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Windows -----------------------------------------------------------------
|
// --- Windows -----------------------------------------------------------------
|
||||||
#elif defined _WIN32
|
#else
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@ -1222,15 +1208,24 @@ append_monitor(struct eizo_monitor *m, HMENU menu, UINT_PTR base)
|
|||||||
AppendMenu(menu, flags_darker, base + IDM_DARKER, L"Darker");
|
AppendMenu(menu, flags_darker, base + IDM_DARKER, L"Darker");
|
||||||
AppendMenu(menu, MF_SEPARATOR, 0, NULL);
|
AppendMenu(menu, MF_SEPARATOR, 0, NULL);
|
||||||
|
|
||||||
uint16_t ports[16] = {0}, current = 0;
|
uint16_t ports[16] = {0};
|
||||||
eizo_get_input_ports(m, ports, sizeof ports / sizeof ports[0] - 1);
|
struct eizo_profile_item *item = &m->profile[EIZO_PROFILE_KEY_INPUT_PORTS];
|
||||||
|
if (item->len) {
|
||||||
|
for (size_t i = 0; i < 15 && i < item->len / 4; i++)
|
||||||
|
ports[i] = peek_u16le(item->data + i * 4);
|
||||||
|
} else {
|
||||||
|
const uint16_t *db = eizo_ports_by_product_name(m->product);
|
||||||
|
for (size_t i = 0; i < 15 && db && db[i]; i++)
|
||||||
|
ports[i] = db[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t current = 0;
|
||||||
(void) eizo_get_input_port(m, ¤t);
|
(void) eizo_get_input_port(m, ¤t);
|
||||||
if (!ports[0])
|
if (!ports[0])
|
||||||
ports[0] = current;
|
ports[0] = current;
|
||||||
|
|
||||||
// USB-C ports are a bit tricky, they only need to be /displayed/ as such.
|
// USB-C ports are a bit tricky, they only need to be /displayed/ as such.
|
||||||
struct eizo_profile_item *item =
|
item = &m->profile[EIZO_PROFILE_KEY_USB_C_INPUT_PORTS];
|
||||||
&m->profile[EIZO_PROFILE_KEY_USB_C_INPUT_PORTS];
|
|
||||||
for (size_t i = 0; ports[i]; i++) {
|
for (size_t i = 0; ports[i]; i++) {
|
||||||
uint8_t usb_c = 0;
|
uint8_t usb_c = 0;
|
||||||
for (size_t u = 0; u < item->len / 2; u++)
|
for (size_t u = 0; u < item->len / 2; u++)
|
||||||
@ -1447,276 +1442,4 @@ wWinMain(
|
|||||||
return msg.wParam;
|
return msg.wParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- macOS -------------------------------------------------------------------
|
|
||||||
#elif defined __APPLE__
|
|
||||||
|
|
||||||
#include <AppKit/AppKit.h>
|
|
||||||
#include <AppKit/NSStatusBar.h>
|
|
||||||
#include <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
static void message_output(const char *format, ...) ATTRIBUTE_PRINTF(1, 2);
|
|
||||||
static void message_error(const char *format, ...) ATTRIBUTE_PRINTF(1, 2);
|
|
||||||
|
|
||||||
static void
|
|
||||||
message_output(const char *format, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
NSString *message = [[NSString alloc]
|
|
||||||
initWithFormat:[NSString stringWithUTF8String: format] arguments:ap];
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
NSAlert *alert = [NSAlert new];
|
|
||||||
[alert setMessageText:message];
|
|
||||||
[alert setAlertStyle:NSAlertStyleInformational];
|
|
||||||
// XXX: How to make the OK button the first responder?
|
|
||||||
[alert addButtonWithTitle:@"OK"];
|
|
||||||
[NSApp activate];
|
|
||||||
[alert.window makeKeyAndOrderFront:nil];
|
|
||||||
[alert runModal];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
message_error(const char *format, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
NSString *message = [[NSString alloc]
|
|
||||||
initWithFormat:[NSString stringWithUTF8String: format] arguments:ap];
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
NSAlert *alert = [NSAlert new];
|
|
||||||
[alert setMessageText:message];
|
|
||||||
[alert setAlertStyle:NSAlertStyleCritical];
|
|
||||||
[alert addButtonWithTitle:@"OK"];
|
|
||||||
[NSApp activate];
|
|
||||||
[alert.window makeKeyAndOrderFront:nil];
|
|
||||||
[alert runModal];
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
|
|
||||||
/// Monitor provides reference counting, and enables use of NSArray.
|
|
||||||
@interface Monitor : NSObject
|
|
||||||
@property (assign, nonatomic) struct eizo_monitor *monitor;
|
|
||||||
- (instancetype)initWithMonitor:(struct eizo_monitor *)monitor;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation Monitor
|
|
||||||
|
|
||||||
- (instancetype)initWithMonitor:(struct eizo_monitor *)monitor {
|
|
||||||
if (self = [super init]) {
|
|
||||||
_monitor = monitor;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc {
|
|
||||||
if (_monitor) {
|
|
||||||
eizo_monitor_close(_monitor);
|
|
||||||
free(_monitor);
|
|
||||||
_monitor = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface ApplicationDelegate
|
|
||||||
: NSObject <NSApplicationDelegate, NSMenuDelegate>
|
|
||||||
@property (strong, nonatomic) NSStatusItem *statusItem;
|
|
||||||
@property (strong, nonatomic) NSMutableArray<Monitor *> *monitors;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation ApplicationDelegate
|
|
||||||
|
|
||||||
- (Monitor *)getMonitorFrom:(NSControl *)control {
|
|
||||||
NSInteger index = control.tag / 0x1000;
|
|
||||||
if (!self.monitors || index < 0 || index >= self.monitors.count)
|
|
||||||
return nil;
|
|
||||||
return self.monitors[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setBrightness:(NSControl *)sender {
|
|
||||||
Monitor *m = [self getMonitorFrom:sender];
|
|
||||||
if (!m)
|
|
||||||
return;
|
|
||||||
eizo_set_brightness(m.monitor, sender.doubleValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setInputPort:(NSControl *)sender {
|
|
||||||
Monitor *m = [self getMonitorFrom:sender];
|
|
||||||
NSUInteger input = sender.tag % 0x1000;
|
|
||||||
if (!m)
|
|
||||||
return;
|
|
||||||
eizo_set_input_port(m.monitor, input);
|
|
||||||
|
|
||||||
NSEventModifierFlags mods = [NSEvent modifierFlags];
|
|
||||||
if (mods & NSEventModifierFlagShift) {
|
|
||||||
NSTask *task = [[NSTask alloc] init];
|
|
||||||
task.launchPath = @"/usr/bin/pmset";
|
|
||||||
task.arguments = @[@"sleepnow"];
|
|
||||||
[task launch];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)appendMonitor:(Monitor *)m toMenu:(NSMenu *)menu base:(NSInteger)base {
|
|
||||||
NSMenuItem *titleItem = [NSMenuItem new];
|
|
||||||
titleItem.attributedTitle = [[NSAttributedString alloc]
|
|
||||||
initWithString:[NSString stringWithFormat:@"%s %s",
|
|
||||||
m.monitor->product, m.monitor->serial]
|
|
||||||
attributes:@{ NSFontAttributeName: [NSFont boldSystemFontOfSize:0] }];
|
|
||||||
[menu addItem:titleItem];
|
|
||||||
[menu addItem:[NSMenuItem separatorItem]];
|
|
||||||
[menu addItem:[NSMenuItem sectionHeaderWithTitle:@"Brightness"]];
|
|
||||||
|
|
||||||
double brightness = 0;
|
|
||||||
(void) eizo_get_brightness(m.monitor, &brightness);
|
|
||||||
|
|
||||||
// XXX: So, while having a slider is strictly more useful,
|
|
||||||
// this is not something you're supposed to do in AppKit, if only because:
|
|
||||||
// - It does not respond to keyboard.
|
|
||||||
// - Positioning it properly is dark magic.
|
|
||||||
NSSlider *slider = [NSSlider
|
|
||||||
sliderWithValue:brightness minValue:0. maxValue:1.
|
|
||||||
target:self action:@selector(setBrightness:)];
|
|
||||||
slider.tag = base;
|
|
||||||
slider.continuous = true;
|
|
||||||
|
|
||||||
NSView *sliderView = [[NSView alloc]
|
|
||||||
initWithFrame:NSMakeRect(0, 0, 200., slider.knobThickness + 2.)];
|
|
||||||
[sliderView addSubview:slider];
|
|
||||||
slider.translatesAutoresizingMaskIntoConstraints = false;
|
|
||||||
[NSLayoutConstraint activateConstraints:@[
|
|
||||||
[slider.leftAnchor
|
|
||||||
constraintEqualToAnchor:sliderView.leftAnchor constant:+23.],
|
|
||||||
[slider.rightAnchor
|
|
||||||
constraintEqualToAnchor:sliderView.rightAnchor constant:-6.],
|
|
||||||
[slider.centerYAnchor
|
|
||||||
constraintEqualToAnchor:sliderView.centerYAnchor]
|
|
||||||
]];
|
|
||||||
|
|
||||||
NSMenuItem *brightnessItem = [[NSMenuItem alloc]
|
|
||||||
initWithTitle:@"" action:nil keyEquivalent:@""];
|
|
||||||
brightnessItem.view = sliderView;
|
|
||||||
|
|
||||||
[menu addItem:brightnessItem];
|
|
||||||
[menu addItem:[NSMenuItem separatorItem]];
|
|
||||||
[menu addItem:[NSMenuItem sectionHeaderWithTitle:@"Input ports"]];
|
|
||||||
|
|
||||||
uint16_t ports[16] = {0}, current = 0;
|
|
||||||
eizo_get_input_ports(m.monitor, ports, sizeof ports / sizeof ports[0] - 1);
|
|
||||||
(void) eizo_get_input_port(m.monitor, ¤t);
|
|
||||||
if (!ports[0])
|
|
||||||
ports[0] = current;
|
|
||||||
|
|
||||||
// USB-C ports are a bit tricky, they only need to be /displayed/ as such.
|
|
||||||
struct eizo_profile_item *item =
|
|
||||||
&m.monitor->profile[EIZO_PROFILE_KEY_USB_C_INPUT_PORTS];
|
|
||||||
for (size_t i = 0; ports[i]; i++) {
|
|
||||||
uint8_t usb_c = 0;
|
|
||||||
for (size_t u = 0; u < item->len / 2; u++)
|
|
||||||
if (ports[i] == peek_u16le(item->data + u * 2))
|
|
||||||
usb_c = u + 1;
|
|
||||||
|
|
||||||
NSString *title = nil;
|
|
||||||
if (!usb_c)
|
|
||||||
title = [NSString stringWithUTF8String:eizo_port_to_name(ports[i])];
|
|
||||||
else if (usb_c == 1)
|
|
||||||
title = [NSString stringWithUTF8String:g_port_names_usb_c[0]];
|
|
||||||
else
|
|
||||||
title = [NSString stringWithFormat:@"%s %u",
|
|
||||||
g_port_names_usb_c[0], usb_c];
|
|
||||||
|
|
||||||
NSMenuItem *inputPortItem = [[NSMenuItem alloc]
|
|
||||||
initWithTitle:title action:@selector(setInputPort:)
|
|
||||||
keyEquivalent:@""];
|
|
||||||
inputPortItem.tag = base + ports[i];
|
|
||||||
if (ports[i] == current)
|
|
||||||
inputPortItem.state = NSControlStateValueOn;
|
|
||||||
[menu addItem:inputPortItem];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)showMenu {
|
|
||||||
struct hid_device_info *devs = hid_enumerate(USB_VID_EIZO, 0);
|
|
||||||
NSMutableArray<Monitor *> *monitors = [NSMutableArray array];
|
|
||||||
NSMenu *menu = [NSMenu new];
|
|
||||||
[menu setDelegate:self];
|
|
||||||
for (struct hid_device_info *p = devs; p; p = p->next) {
|
|
||||||
struct eizo_monitor *m = calloc(1, sizeof *m);
|
|
||||||
if (!m)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!eizo_monitor_open(m, p)) {
|
|
||||||
message_error("%s", m->error);
|
|
||||||
free(m);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Monitor *monitor = [[Monitor alloc] initWithMonitor:m];
|
|
||||||
[self appendMonitor:monitor toMenu:menu base:0x1000 * monitors.count];
|
|
||||||
[menu addItem:[NSMenuItem separatorItem]];
|
|
||||||
[monitors addObject:monitor];
|
|
||||||
}
|
|
||||||
if (!monitors.count) {
|
|
||||||
NSMenuItem *item = [[NSMenuItem alloc]
|
|
||||||
initWithTitle:@"No monitors found" action:nil keyEquivalent:@""];
|
|
||||||
item.enabled = false;
|
|
||||||
[menu addItem:item];
|
|
||||||
}
|
|
||||||
|
|
||||||
[menu addItem:[NSMenuItem separatorItem]];
|
|
||||||
[menu addItem:[[NSMenuItem alloc]
|
|
||||||
initWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"]];
|
|
||||||
|
|
||||||
self.monitors = monitors;
|
|
||||||
|
|
||||||
// XXX: Unfortunately, this is not how menus should behave,
|
|
||||||
// but we really want to generate the menu on demand.
|
|
||||||
self.statusItem.menu = menu;
|
|
||||||
[self.statusItem.button performClick:nil];
|
|
||||||
self.statusItem.menu = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)menuDidClose:(NSMenu *)menu {
|
|
||||||
// Close and free up the devices as soon as possible, but no sooner.
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
self.monitors = nil;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
|
|
||||||
NSStatusBar *systemBar = [NSStatusBar systemStatusBar];
|
|
||||||
self.statusItem = [systemBar statusItemWithLength:NSSquareStatusItemLength];
|
|
||||||
if (!self.statusItem.button)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Not bothering with templates,
|
|
||||||
// the icon would need to have a hole through it to look better.
|
|
||||||
NSImage *image = [NSApp applicationIconImage];
|
|
||||||
// One would expect the status bar to pick a reasonable size
|
|
||||||
// automatically, but that is not what happens.
|
|
||||||
image.size = NSMakeSize(systemBar.thickness, systemBar.thickness);
|
|
||||||
self.statusItem.button.image = image;
|
|
||||||
self.statusItem.button.action = @selector(showMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
@autoreleasepool {
|
|
||||||
if (argc > 1)
|
|
||||||
return run(argc, argv, message_output, message_error, true);
|
|
||||||
|
|
||||||
NSApplication *app = [NSApplication sharedApplication];
|
|
||||||
ApplicationDelegate *delegate = [ApplicationDelegate new];
|
|
||||||
app.delegate = delegate;
|
|
||||||
[app setActivationPolicy:NSApplicationActivationPolicyAccessory];
|
|
||||||
[app run];
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
Before Width: | Height: | Size: 7.7 KiB |
BIN
eizoctltray.png
Normal file
BIN
eizoctltray.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.7 KiB |
Loading…
x
Reference in New Issue
Block a user