Index: .fossil-settings/ignore-glob ================================================================== --- .fossil-settings/ignore-glob +++ .fossil-settings/ignore-glob @@ -47,10 +47,11 @@ tests/gamecontroller/boot.dol tests/gamecontroller/tests tests/gamecontroller/tests.3dsx tests/gamecontroller/tests.arm9 tests/gamecontroller/tests.nds +tests/gamecontroller/tests.nro tests/iOS.xcodeproj/*.pbxuser tests/iOS.xcodeproj/project.xcworkspace tests/iOS.xcodeproj/xcuserdata tests/objc_sync/objc_sync tests/plugin/Info.plist Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -285,10 +285,11 @@ enable_sockets="no" # TODO check_pedantic="no" AC_DEFINE(OF_NINTENDO_SWITCH, 1, [Whether we are compiling for Nintendo Switch]) + AC_SUBST(USE_SRCS_NINTENDO_SWITCH, '${SRCS_NINTENDO_SWITCH}') ]) CPP="$OBJCPP" CPPFLAGS="$CPPFLAGS $OBJCPPFLAGS -DOF_COMPILING_OBJFW" flags="-fexceptions -fobjc-exceptions -funwind-tables" Index: extra.mk.in ================================================================== --- extra.mk.in +++ extra.mk.in @@ -100,10 +100,11 @@ USE_SRCS_EVDEV = @USE_SRCS_EVDEV@ USE_SRCS_FILES = @USE_SRCS_FILES@ USE_SRCS_IPX = @USE_SRCS_IPX@ USE_SRCS_NINTENDO_3DS = @USE_SRCS_NINTENDO_3DS@ USE_SRCS_NINTENDO_DS = @USE_SRCS_NINTENDO_DS@ +USE_SRCS_NINTENDO_SWITCH = @USE_SRCS_NINTENDO_SWITCH@ USE_SRCS_PLUGINS = @USE_SRCS_PLUGINS@ USE_SRCS_SCTP = @USE_SRCS_SCTP@ USE_SRCS_SOCKETS = @USE_SRCS_SOCKETS@ USE_SRCS_SUBPROCESSES = @USE_SRCS_SUBPROCESSES@ USE_SRCS_TAGGED_POINTERS = @USE_SRCS_TAGGED_POINTERS@ Index: src/hid/Makefile ================================================================== --- src/hid/Makefile +++ src/hid/Makefile @@ -16,10 +16,11 @@ OHGameControllerDirectionalPad.m \ OHGameControllerElement.m \ ${USE_SRCS_EVDEV} \ ${USE_SRCS_NINTENDO_3DS} \ ${USE_SRCS_NINTENDO_DS} \ + ${USE_SRCS_NINTENDO_SWITCH} \ ${USE_SRCS_WII} \ ${USE_SRCS_XINPUT} SRCS_EVDEV = OHEvdevDualSense.m \ OHEvdevDualShock4.m \ OHEvdevExtendedGamepad.m \ @@ -28,10 +29,12 @@ OHEvdevStadiaExtendedGamepad.m SRCS_NINTENDO_3DS = OHNintendo3DSExtendedGamepad.m \ OHNintendo3DSGameController.m SRCS_NINTENDO_DS = OHNintendoDSGamepad.m \ OHNintendoDSGameController.m +SRCS_NINTENDO_SWITCH = OHNintendoSwitchExtendedGamepad.m \ + OHNintendoSwitchGameController.m SRCS_WII = OHWiiClassicController.m \ OHWiiGameController.m SRCS_XINPUT = OHXInputExtendedGamepad.m \ OHXInputGameController.m Index: src/hid/OHGameController.m ================================================================== --- src/hid/OHGameController.m +++ src/hid/OHGameController.m @@ -37,10 +37,13 @@ # import "OHNintendo3DSGameController.h" #endif #ifdef OF_WII # import "OHWiiGameController.h" #endif +#ifdef OF_NINTENDO_SWITCH +# import "OHNintendoSwitchGameController.h" +#endif const uint16_t OHVendorIDSony = 0x054C; const uint16_t OHVendorIDNintendo = 0x057E; const uint16_t OHVendorIDGoogle = 0x18D1; const uint16_t OHProductIDDualShock4 = 0x09CC; @@ -63,10 +66,12 @@ return [OHNintendoDSGameController controllers]; #elif defined(OF_NINTENDO_3DS) return [OHNintendo3DSGameController controllers]; #elif defined(OF_WII) return [OHWiiGameController controllers]; +#elif defined(OF_NINTENDO_SWITCH) + return [OHNintendoSwitchGameController controllers]; #else return [OFArray array]; #endif } ADDED src/hid/OHNintendoSwitchExtendedGamepad.h Index: src/hid/OHNintendoSwitchExtendedGamepad.h ================================================================== --- /dev/null +++ src/hid/OHNintendoSwitchExtendedGamepad.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3.0 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * version 3.0 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3.0 along with this program. If not, see + * . + */ + +#import "OHExtendedGamepad.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OHNintendoSwitchExtendedGamepad: OFObject +{ + OFDictionary OF_GENERIC(OFString *, OHGameControllerButton *) *_buttons; + OFDictionary OF_GENERIC(OFString *, OHGameControllerDirectionalPad *) + *_directionalPads; +} +@end + +OF_ASSUME_NONNULL_END ADDED src/hid/OHNintendoSwitchExtendedGamepad.m Index: src/hid/OHNintendoSwitchExtendedGamepad.m ================================================================== --- /dev/null +++ src/hid/OHNintendoSwitchExtendedGamepad.m @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3.0 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * version 3.0 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3.0 along with this program. If not, see + * . + */ + +#include "config.h" + +#import "OHNintendoSwitchExtendedGamepad.h" +#import "OFDictionary.h" +#import "OHGameControllerAxis.h" +#import "OHGameControllerButton.h" +#import "OHGameControllerDirectionalPad.h" + +static OFString *const buttonNames[] = { + @"A", @"B", @"X", @"Y", @"L", @"R", @"ZL", @"ZR", @"Left Thumbstick", + @"Right Thumbstick", @"+", @"-" +}; +static const size_t numButtons = sizeof(buttonNames) / sizeof(*buttonNames); + +@implementation OHNintendoSwitchExtendedGamepad +@synthesize buttons = _buttons, directionalPads = _directionalPads; + +- (instancetype)init +{ + self = [super init]; + + @try { + void *pool = objc_autoreleasePoolPush(); + OFMutableDictionary *buttons = + [OFMutableDictionary dictionaryWithCapacity: numButtons]; + OFMutableDictionary *directionalPads; + OHGameControllerAxis *xAxis, *yAxis; + OHGameControllerDirectionalPad *directionalPad; + OHGameControllerButton *up, *down, *left, *right; + + for (size_t i = 0; i < numButtons; i++) { + OHGameControllerButton *button = + [[[OHGameControllerButton alloc] + initWithName: buttonNames[i]] autorelease]; + [buttons setObject: button forKey: buttonNames[i]]; + } + [buttons makeImmutable]; + _buttons = [buttons retain]; + + directionalPads = + [OFMutableDictionary dictionaryWithCapacity: 3]; + + xAxis = [[[OHGameControllerAxis alloc] + initWithName: @"X"] autorelease]; + yAxis = [[[OHGameControllerAxis alloc] + initWithName: @"Y"] autorelease]; + directionalPad = [[[OHGameControllerDirectionalPad alloc] + initWithName: @"Left Thumbstick" + xAxis: xAxis + yAxis: yAxis] autorelease]; + [directionalPads setObject: directionalPad + forKey: @"Left Thumbstick"]; + + xAxis = [[[OHGameControllerAxis alloc] + initWithName: @"RX"] autorelease]; + yAxis = [[[OHGameControllerAxis alloc] + initWithName: @"RY"] autorelease]; + directionalPad = [[[OHGameControllerDirectionalPad alloc] + initWithName: @"Right Thumbstick" + xAxis: xAxis + yAxis: yAxis] autorelease]; + [directionalPads setObject: directionalPad + forKey: @"Right Thumbstick"]; + + up = [[[OHGameControllerButton alloc] + initWithName: @"D-Pad Up"] autorelease]; + down = [[[OHGameControllerButton alloc] + initWithName: @"D-Pad Down"] autorelease]; + left = [[[OHGameControllerButton alloc] + initWithName: @"D-Pad Left"] autorelease]; + right = [[[OHGameControllerButton alloc] + initWithName: @"D-Pad Right"] autorelease]; + directionalPad = [[[OHGameControllerDirectionalPad alloc] + initWithName: @"D-Pad" + up: up + down: down + left: left + right: right] autorelease]; + [directionalPads setObject: directionalPad forKey: @"D-Pad"]; + + [directionalPads makeImmutable]; + _directionalPads = [directionalPads retain]; + + objc_autoreleasePoolPop(pool); + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_buttons release]; + [_directionalPads release]; + + [super dealloc]; +} + +- (OFDictionary OF_GENERIC(OFString *, OHGameControllerAxis *) *)axes +{ + return [OFDictionary dictionary]; +} + +- (OHGameControllerButton *)northButton +{ + return [_buttons objectForKey: @"X"]; +} + +- (OHGameControllerButton *)southButton +{ + return [_buttons objectForKey: @"B"]; +} + +- (OHGameControllerButton *)westButton +{ + return [_buttons objectForKey: @"Y"]; +} + +- (OHGameControllerButton *)eastButton +{ + return [_buttons objectForKey: @"A"]; +} + +- (OHGameControllerButton *)leftShoulderButton +{ + return [_buttons objectForKey: @"L"]; +} + +- (OHGameControllerButton *)rightShoulderButton +{ + return [_buttons objectForKey: @"R"]; +} + +- (OHGameControllerButton *)leftTriggerButton +{ + return [_buttons objectForKey: @"ZL"]; +} + +- (OHGameControllerButton *)rightTriggerButton +{ + return [_buttons objectForKey: @"ZR"]; +} + +- (OHGameControllerButton *)leftThumbstickButton +{ + return [_buttons objectForKey: @"Left Thumbstick"]; +} + +- (OHGameControllerButton *)rightThumbstickButton +{ + return [_buttons objectForKey: @"Right Thumbstick"]; +} + +- (OHGameControllerButton *)menuButton +{ + return [_buttons objectForKey: @"+"]; +} + +- (OHGameControllerButton *)optionsButton +{ + return [_buttons objectForKey: @"-"]; +} + +- (OHGameControllerButton *)homeButton +{ + return nil; +} + +- (OHGameControllerDirectionalPad *)leftThumbstick +{ + return [_directionalPads objectForKey: @"Left Thumbstick"]; +} + +- (OHGameControllerDirectionalPad *)rightThumbstick +{ + return [_directionalPads objectForKey: @"Right Thumbstick"]; +} + +- (OHGameControllerDirectionalPad *)dPad +{ + return [_directionalPads objectForKey: @"D-Pad"]; +} +@end ADDED src/hid/OHNintendoSwitchGameController.h Index: src/hid/OHNintendoSwitchGameController.h ================================================================== --- /dev/null +++ src/hid/OHNintendoSwitchGameController.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3.0 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * version 3.0 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3.0 along with this program. If not, see + * . + */ + +#import "OHGameController.h" + +#define id nx_id +#include +#undef id + +OF_ASSUME_NONNULL_BEGIN + +@class OHNintendoSwitchExtendedGamepad; + +@interface OHNintendoSwitchGameController: OHGameController +{ + PadState _padState; + OHNintendoSwitchExtendedGamepad *_extendedGamepad; +} + +- (instancetype)initWithIndex: (size_t)index; +@end + +OF_ASSUME_NONNULL_END ADDED src/hid/OHNintendoSwitchGameController.m Index: src/hid/OHNintendoSwitchGameController.m ================================================================== --- /dev/null +++ src/hid/OHNintendoSwitchGameController.m @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3.0 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * version 3.0 for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * version 3.0 along with this program. If not, see + * . + */ + +#include "config.h" + +#import "OHNintendoSwitchGameController.h" +#import "OFArray.h" +#import "OFDictionary.h" +#import "OHGameControllerAxis.h" +#import "OHGameControllerButton.h" +#import "OHGameControllerDirectionalPad.h" +#import "OHNintendoSwitchExtendedGamepad.h" + +#import "OFInitializationFailedException.h" +#import "OFReadFailedException.h" + +#define id nx_id +#include +#undef id + +static const size_t maxControllers = 8; + +@implementation OHNintendoSwitchGameController +@synthesize extendedGamepad = _extendedGamepad; + ++ (void)initialize +{ + if (self == [OHNintendoSwitchGameController class]) + padConfigureInput(maxControllers, HidNpadStyleSet_NpadFullCtrl); +} + ++ (OFArray OF_GENERIC(OHGameController *) *)controllers +{ + OFMutableArray *controllers = [OFMutableArray array]; + void *pool = objc_autoreleasePoolPush(); + + for (size_t i = 0; i < maxControllers; i++) { + OHGameController *controller; + + @try { + controller = [[[OHNintendoSwitchGameController alloc] + initWithIndex: i] autorelease]; + } @catch (OFInitializationFailedException *e) { + /* Controller does not exist. */ + continue; + } + + [controllers addObject: controller]; + } + + [controllers makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return controllers; +} + +- (instancetype)initWithIndex: (size_t)index +{ + self = [super init]; + + @try { + padInitialize(&_padState, HidNpadIdType_No1 + index, + (index == 0 ? HidNpadIdType_Handheld : 0)); + padUpdate(&_padState); + + if (!(padGetAttributes(&_padState) & + HidNpadAttribute_IsConnected)) + @throw [OFInitializationFailedException + exceptionWithClass: self.class]; + + _extendedGamepad = + [[OHNintendoSwitchExtendedGamepad alloc] init]; + + [self retrieveState]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_extendedGamepad release]; + + [super dealloc]; +} + +- (void)retrieveState +{ + void *pool = objc_autoreleasePoolPush(); + OFDictionary OF_GENERIC(OFString *, OHGameControllerButton *) + *buttons = _extendedGamepad.buttons; + OFDictionary OF_GENERIC(OFString *, OHGameControllerDirectionalPad *) + *directionalPads = _extendedGamepad.directionalPads; + u64 keys; + HidAnalogStickState stick; + OHGameControllerDirectionalPad *directionalPad; + + padUpdate(&_padState); + keys = padGetButtons(&_padState); + + [[buttons objectForKey: @"A"] setValue: !!(keys & HidNpadButton_A)]; + [[buttons objectForKey: @"B"] setValue: !!(keys & HidNpadButton_B)]; + [[buttons objectForKey: @"X"] setValue: !!(keys & HidNpadButton_X)]; + [[buttons objectForKey: @"Y"] setValue: !!(keys & HidNpadButton_Y)]; + [[buttons objectForKey: @"L"] setValue: !!(keys & HidNpadButton_L)]; + [[buttons objectForKey: @"R"] setValue: !!(keys & HidNpadButton_R)]; + [[buttons objectForKey: @"ZL"] setValue: !!(keys & HidNpadButton_ZL)]; + [[buttons objectForKey: @"ZR"] setValue: !!(keys & HidNpadButton_ZR)]; + [[buttons objectForKey: @"Left Thumbstick"] + setValue: !!(keys & HidNpadButton_StickL)]; + [[buttons objectForKey: @"Right Thumbstick"] + setValue: !!(keys & HidNpadButton_StickR)]; + [[buttons objectForKey: @"+"] setValue: !!(keys & HidNpadButton_Plus)]; + [[buttons objectForKey: @"-"] setValue: !!(keys & HidNpadButton_Minus)]; + + stick = padGetStickPos(&_padState, 0); + directionalPad = [directionalPads objectForKey: @"Left Thumbstick"]; + [directionalPad.xAxis setValue: + (float)stick.x / (stick.x < 0 ? -INT16_MIN : INT16_MAX)]; + [directionalPad.yAxis setValue: + -(float)stick.y / (stick.y < 0 ? -INT16_MIN : INT16_MAX)]; + + stick = padGetStickPos(&_padState, 1); + directionalPad = [directionalPads objectForKey: @"Right Thumbstick"]; + [directionalPad.xAxis setValue: + (float)stick.x / (stick.x < 0 ? -INT16_MIN : INT16_MAX)]; + [directionalPad.yAxis setValue: + -(float)stick.y / (stick.y < 0 ? -INT16_MIN : INT16_MAX)]; + + directionalPad = [directionalPads objectForKey: @"D-Pad"]; + [directionalPad.up setValue: !!(keys & keys & HidNpadButton_Up)]; + [directionalPad.down setValue: !!(keys & keys & HidNpadButton_Down)]; + [directionalPad.left setValue: !!(keys & keys & HidNpadButton_Left)]; + [directionalPad.right setValue: !!(keys & keys & HidNpadButton_Right)]; + + objc_autoreleasePoolPop(pool); +} + +- (OFString *)name +{ + return @"Nintendo Switch"; +} + +- (id )rawProfile +{ + return _extendedGamepad; +} + +- (id )gamepad +{ + return _extendedGamepad; +} +@end Index: src/test/OTAppDelegate.m ================================================================== --- src/test/OTAppDelegate.m +++ src/test/OTAppDelegate.m @@ -112,11 +112,10 @@ CFRelease(resourcesURL); #elif defined(OF_WII) || defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) [OFStdIOStream setUpConsole]; #elif defined(OF_NINTENDO_SWITCH) consoleInit(NULL); - padConfigureInput(1, HidNpadStyleSet_NpadStandard); updateConsole(true); #endif } - (OFMutableSet OF_GENERIC(Class) *)testClasses @@ -277,15 +276,20 @@ break; } if (status == StatusFailed) { -#if defined(OF_WII) || defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) +#if defined(OF_WII) || defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) || \ + defined(OF_NINTENDO_SWITCH) [OFStdOut setForegroundColor: [OFColor silver]]; [OFStdOut writeLine: @"Press A to continue"]; +# ifdef OF_NINTENDO_SWITCH + while (appletMainLoop()) { +# else for (;;) { +# endif void *pool = objc_autoreleasePoolPush(); OHGameController *controller = [[OHGameController controllers] objectAtIndex: 0]; OHGameControllerButton *button = [controller.rawProfile.buttons objectForKey: @"A"]; @@ -293,27 +297,18 @@ [controller retrieveState]; if (button.pressed) break; +# ifdef OF_NINTENDO_SWITCH + updateConsole(true); +# else [OFThread waitForVerticalBlank]; +# endif objc_autoreleasePoolPop(pool); } -#elif defined(OF_NINTENDO_SWITCH) - [OFStdOut setForegroundColor: [OFColor silver]]; - [OFStdOut writeLine: @"Press A to continue"]; - - while (appletMainLoop()) { - PadState pad; - - padUpdate(&pad); - updateConsole(true); - - if (padGetButtonsDown(&pad) & HidNpadButton_A) - break; - } #endif } } - (OFString *)descriptionForException: (id)exception Index: tests/gamecontroller/GameControllerTests.m ================================================================== --- tests/gamecontroller/GameControllerTests.m +++ tests/gamecontroller/GameControllerTests.m @@ -45,15 +45,22 @@ static size_t buttonsPerLine = 3; #else static size_t buttonsPerLine = 5; #endif -#if defined(OF_WII) || defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) +#if defined(OF_WII) || defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) || \ + defined(OF_NINTENDO_SWITCH) # define red maroon # define yellow olive # define gray silver #endif + +#ifdef OF_NINTENDO_SWITCH +# define id nx_id +# include +# undef id +#endif @interface GameControllerTests: OFObject { OFArray OF_GENERIC(OHGameController *) *_controllers; OFDate *_lastControllersUpdate; @@ -178,12 +185,19 @@ - (void)applicationDidFinishLaunching: (OFNotification *)notification { #if defined(OF_WII) || defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) [OFStdIOStream setUpConsole]; #endif +#if defined(OF_NINTENDO_SWITCH) + consoleInit(NULL); +#endif +#ifdef OF_NINTENDO_SWITCH + while (appletMainLoop()) { +#else for (;;) { +#endif void *pool = objc_autoreleasePoolPush(); OHGameController *leftJoyCon = nil, *rightJoyCon = nil; if (_lastControllersUpdate == nil || -[_lastControllersUpdate timeIntervalSinceNow] > 1) { @@ -242,13 +256,15 @@ printProfile(combinedJoyCons); } #if defined(OF_WII) || defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) [OFThread waitForVerticalBlank]; +#elif defined(OF_NINTENDO_SWITCH) + consoleUpdate(NULL); #else [OFThread sleepForTimeInterval: 1.f / 60.f]; #endif objc_autoreleasePoolPop(pool); } } @end Index: tests/gamecontroller/Makefile ================================================================== --- tests/gamecontroller/Makefile +++ tests/gamecontroller/Makefile @@ -1,11 +1,12 @@ include ../../extra.mk CLEAN = boot.dol \ ${PROG_NOINST}.3dsx \ ${PROG_NOINST}.arm9 \ - ${PROG_NOINST}.ndsd \ + ${PROG_NOINST}.nds \ + ${PROG_NOINST}.nro PROG_NOINST = tests${PROG_SUFFIX} SRCS = GameControllerTests.m include ../../buildsys.mk @@ -107,10 +108,13 @@ ${PROG_NOINST}.arm9: ${PROG_NOINST} arm-none-eabi-objcopy -O binary $< $@ ${PROG_NOINST}.nds: ${PROG_NOINST}.arm9 ndstool -c $@ -9 ${PROG_NOINST} + +${PROG_NOINST}.nro: ${PROG_NOINST} + elf2nro ${PROG_NOINST} $@ CPPFLAGS += -I../../src \ -I../../src/exceptions \ -I../../src/hid \ -I../../src/runtime \