diff --git a/CMakeLists.txt b/CMakeLists.txt index 28d99f2e6..b498355f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ else() set(PROJECT_VERSION_MAJOR ${VERSION_MAJOR}) set(PROJECT_VERSION_MINOR ${VERSION_MINOR}) set(PROJECT_VERSION_PATCH ${VERSION_PATCH}) + set(PROJECT_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake) @@ -149,6 +150,19 @@ else() set(ZLIB_FOUND NO) endif() +if(TARGET_OS AND TARGET_OS STREQUAL "mac") + find_program(DMG dmg) + find_program(HFSPLUS hfsplus) + find_program(NEWFS_HFS newfs_hfs) + if(DMG AND HFSPLUS AND NEWFS_HFS) + set(DMGTOOLS_FOUND ON) + else() + set(DMGTOOLS_FOUND OFF) + endif() + + find_program(HDIUTIL hdiutil) +endif() + message(STATUS "******** DDNet ********") message(STATUS "Target OS: ${TARGET_OS} ${TARGET_BITS}bit") message(STATUS "Compiler: ${CMAKE_CXX_COMPILER}") @@ -169,11 +183,17 @@ function(show_dependency_status NAME FOUND PATH) endfunction() show_dependency_status("Curl" ${CURL_FOUND} "${CURL_LIBRARY}") +if(TARGET_OS AND TARGET_OS STREQUAL "mac") + show_dependency_status("Dmg tools" ${DMGTOOLS_FOUND} "") +endif() show_dependency_status("Freetype" ${FREETYPE_FOUND} "${FREETYPE_LIBRARY}") if(DOWNLOAD_GTEST) show_dependency_status("Git" ${GIT_FOUND} "${GIT_EXECUTABLE}") endif() show_dependency_status("GTest" ${GTEST_FOUND} "${GTEST_LIBRARY}") +if(TARGET_OS AND TARGET_OS STREQUAL "mac") + show_dependency_status("Hdiutil" ${HDIUTIL} "") +endif() if(MYSQL) show_dependency_status("MySQL" ${MYSQL_FOUND} "${MYSQL_LIBRARY}") endif() @@ -935,6 +955,15 @@ target_link_libraries(${TARGET_SERVER} ${LIBS_SERVER}) list(APPEND TARGETS_OWN ${TARGET_SERVER}) list(APPEND TARGETS_LINK ${TARGET_SERVER}) +if(TARGET_OS AND TARGET_OS STREQUAL "mac") + set(SERVER_LAUNCHER_SRC src/osxlaunch/server.mm) + set(TARGET_SERVER_LAUNCHER ${TARGET_SERVER}-Launcher) + add_executable(${TARGET_SERVER_LAUNCHER} ${SERVER_LAUNCHER_SRC}) + target_link_libraries(${TARGET_SERVER_LAUNCHER} ${COCOA}) + list(APPEND TARGETS_OWN ${TARGET_SERVER_LAUNCHER}) + list(APPEND TARGETS_LINK ${TARGET_SERVER_LAUNCHER}) +endif() + ######################################################################## # VARIOUS TARGETS ######################################################################## @@ -1146,6 +1175,7 @@ if(TARGET_OS AND TARGET_BITS) endif() set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_SYSTEM_NAME}) +set(CPACK_ARCHIVE_PORTABLE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME}) set(CPACK_SOURCE_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-src) set(CPACK_SOURCE_FILES CMakeLists.txt @@ -1201,7 +1231,7 @@ if(TARGET_OS STREQUAL "windows") endif() if(CMAKE_VERSION VERSION_GREATER 3.6 OR CMAKE_VERSION VERSION_EQUAL 3.6) - set(EXTRA_ARGS DESTINATION ${CPACK_PACKAGE_FILE_NAME} COMPONENT portable) + set(EXTRA_ARGS DESTINATION ${CPACK_PACKAGE_FILE_NAME} COMPONENT portable EXCLUDE_FROM_ALL) install(TARGETS ${CPACK_TARGETS} ${EXTRA_ARGS}) install(DIRECTORY ${CPACK_DIRS} ${EXTRA_ARGS}) install(FILES ${CPACK_FILES} ${EXTRA_ARGS}) @@ -1209,23 +1239,97 @@ else() message(WARNING "Cannot create CPack targets, CMake version too old. Use CMake 3.6 or newer.") endif() -set(COPY_FILE_COMMANDS) -set(COPY_DIR_COMMANDS) -set(COPY_TARGET_COMMANDS) -foreach(file ${CPACK_FILES}) - list(APPEND COPY_FILE_COMMANDS COMMAND cmake -E copy ${PROJECT_SOURCE_DIR}/${file} ${CPACK_PACKAGE_FILE_NAME}/) -endforeach() -foreach(dir ${CPACK_DIRS}) - list(APPEND COPY_DIR_COMMAND COMMAND cmake -E copy_directory ${PROJECT_SOURCE_DIR}/${dir} ${CPACK_PACKAGE_FILE_NAME}/${dir}) -endforeach() -foreach(target ${CPACK_TARGETS}) - list(APPEND COPY_TARGET_COMMANDS COMMAND cmake -E copy $ ${CPACK_PACKAGE_FILE_NAME}/) -endforeach() +set(PACKAGE_TARGETS) + +if(DMGTOOLS_FOUND OR HDIUTIL) + file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/bundle/client/") + file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/bundle/server/") + configure_file(other/bundle/client/Info.plist.in bundle/client/Info.plist) + configure_file(other/bundle/server/Info.plist.in bundle/server/Info.plist) + + if(HDIUTIL) + set(DMG_PARAMS --hdiutil ${HDIUTIL}) + elseif(DMGTOOLS_FOUND) + set(DMG_PARAMS --dmgtools ${DMG} ${HFSPLUS} ${NEWFS_HFS}) + endif() + set(DMG_TMPDIR pack_${CPACK_PACKAGE_FILE_NAME}_dmg) + set(DMG_MKDIRS + ${TARGET_CLIENT}.app + ${TARGET_CLIENT}.app/Contents + ${TARGET_CLIENT}.app/Contents/Frameworks + ${TARGET_CLIENT}.app/Contents/MacOS + ${TARGET_CLIENT}.app/Contents/Resources + ${TARGET_SERVER}.app + ${TARGET_SERVER}.app/Contents + ${TARGET_SERVER}.app/Contents/MacOS + ${TARGET_SERVER}.app/Contents/Resources + ${TARGET_SERVER}.app/Contents/Resources/data + ${TARGET_SERVER}.app/Contents/Resources/data/mapres + ) + set(DMG_MKDIR_COMMANDS) + foreach(dir ${DMG_MKDIRS}) + list(APPEND DMG_MKDIR_COMMANDS COMMAND ${CMAKE_COMMAND} -E make_directory ${DMG_TMPDIR}/${dir}) + endforeach() + add_custom_command(OUTPUT ${CPACK_PACKAGE_FILE_NAME}.dmg + COMMAND ${CMAKE_COMMAND} -E remove_directory ${DMG_TMPDIR} + ${DMG_MKDIR_COMMANDS} + + # CLIENT + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/data ${DMG_TMPDIR}/${TARGET_CLIENT}.app/Contents/Resources/data + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/other/icons/${TARGET_CLIENT}.icns ${DMG_TMPDIR}/${TARGET_CLIENT}.app/Contents/Resources/ + COMMAND ${CMAKE_COMMAND} -E copy bundle/client/Info.plist ${PROJECT_SOURCE_DIR}/other/bundle/client/PkgInfo ${DMG_TMPDIR}/${TARGET_CLIENT}.app/Contents/ + COMMAND ${CMAKE_COMMAND} -E copy $ ${DMG_TMPDIR}/${TARGET_CLIENT}.app/Contents/MacOS/ + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/ddnet-libs/sdl/mac/lib64/SDL2.framework ${DMG_TMPDIR}/${TARGET_CLIENT}.app/Contents/Frameworks/SDL2.framework + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/ddnet-libs/freetype/mac/lib64/libfreetype.6.dylib ${DMG_TMPDIR}/${TARGET_CLIENT}.app/Contents/Frameworks/ + COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change @rpath/SDL2.framework/Versions/A/SDL2 @executable_path/../Frameworks/SDL2.framework/SDL2 ${DMG_TMPDIR}/${TARGET_CLIENT}.app/Contents/MacOS/${TARGET_CLIENT} + COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change /usr/local/lib/libfreetype.6.dylib @executable_path/../Frameworks/libfreetype.6.dylib ${DMG_TMPDIR}/${TARGET_CLIENT}.app/Contents/MacOS/${TARGET_CLIENT} + + # SERVER + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/data/maps ${DMG_TMPDIR}/${TARGET_SERVER}.app/Contents/Resources/data/maps + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/other/icons/${TARGET_SERVER}.icns ${DMG_TMPDIR}/${TARGET_SERVER}.app/Contents/Resources/ + COMMAND ${CMAKE_COMMAND} -E copy bundle/server/Info.plist ${PROJECT_SOURCE_DIR}/other/bundle/server/PkgInfo ${DMG_TMPDIR}/${TARGET_SERVER}.app/Contents/ + COMMAND ${CMAKE_COMMAND} -E copy $ $ ${DMG_TMPDIR}/${TARGET_SERVER}.app/Contents/MacOS/ + + # DMG + COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/scripts/dmg.py create ${DMG_PARAMS} ${CPACK_PACKAGE_FILE_NAME}.dmg ${CPACK_PACKAGE_FILE_NAME} ${DMG_TMPDIR} + + DEPENDS + ${TARGET_CLIENT} + ${TARGET_SERVER_LAUNCHER} + ${TARGET_SERVER} + ${CMAKE_BINARY_DIR}/bundle/client/Info.plist + ${CMAKE_BINARY_DIR}/bundle/server/Info.plist + data + other/bundle/client/PkgInfo + other/bundle/server/PkgInfo + other/icons/${TARGET_CLIENT}.icns + other/icons/${TARGET_SERVER}.icns + scripts/dmg.py + ) + add_custom_target(package_dmg DEPENDS ${CPACK_PACKAGE_FILE_NAME}.dmg) + list(APPEND PACKAGE_TARGETS package_dmg) +endif() foreach(ext zip tar.gz tar.xz) set(TAR_MODE c) set(TAR_EXTRA_ARGS) string(REPLACE . _ EXT_SLUG ${ext}) + + set(TMPDIR pack_${CPACK_PACKAGE_FILE_NAME}_${EXT_SLUG}/${CPACK_PACKAGE_FILE_NAME}) + + set(COPY_FILE_COMMANDS) + set(COPY_DIR_COMMANDS) + set(COPY_TARGET_COMMANDS) + foreach(file ${CPACK_FILES}) + list(APPEND COPY_FILE_COMMANDS COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/${file} ${TMPDIR}/) + endforeach() + foreach(dir ${CPACK_DIRS}) + list(APPEND COPY_DIR_COMMANDS COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/${dir} ${TMPDIR}/${dir}) + endforeach() + foreach(target ${CPACK_TARGETS}) + list(APPEND COPY_TARGET_COMMANDS COMMAND ${CMAKE_COMMAND} -E copy $ ${TMPDIR}/) + endforeach() + if(ext STREQUAL zip) set(TAR_EXTRA_ARGS --format=zip) elseif(ext STREQUAL tar.gz) @@ -1234,27 +1338,27 @@ foreach(ext zip tar.gz tar.xz) set(TAR_MODE cJ) endif() add_custom_command(OUTPUT ${CPACK_PACKAGE_FILE_NAME}.${ext} - COMMAND cmake -E make_directory ${CPACK_PACKAGE_FILE_NAME} + COMMAND ${CMAKE_COMMAND} -E remove_directory ${TMPDIR} + COMMAND ${CMAKE_COMMAND} -E make_directory ${TMPDIR} ${COPY_FILE_COMMANDS} ${COPY_DIR_COMMANDS} ${COPY_TARGET_COMMANDS} - COMMAND cmake -E tar ${TAR_MODE} ${CPACK_PACKAGE_FILE_NAME}.${ext} ${TAR_EXTRA_ARGS} -- ${CPACK_PACKAGE_FILE_NAME}/ - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E chdir pack_${CPACK_PACKAGE_FILE_NAME}_${EXT_SLUG} ${CMAKE_COMMAND} -E tar ${TAR_MODE} ../${CPACK_PACKAGE_FILE_NAME}.${ext} ${TAR_EXTRA_ARGS} -- ${CPACK_PACKAGE_FILE_NAME}/ + DEPENDS ${CPACK_TARGETS} ) add_custom_target(package_${EXT_SLUG} DEPENDS ${CPACK_PACKAGE_FILE_NAME}.${ext}) + list(APPEND PACKAGE_TARGETS package_${EXT_SLUG}) endforeach() set(PACKAGE_DEFAULT tar_xz) if(TARGET_OS STREQUAL "windows") set(PACKAGE_DEFAULT zip) +elseif(TARGET_OS STREQUAL "mac") + set(PACKAGE_DEFAULT dmg) endif() add_custom_target(package_default DEPENDS package_${PACKAGE_DEFAULT}) -add_custom_target(package_all DEPENDS - package_tar_gz - package_tar_xz - package_zip -) +add_custom_target(package_all DEPENDS ${PACKAGE_TARGETS}) # Unset these variables, they might do something in the future of CPack. unset(CPACK_SOURCE_FILES) diff --git a/README.md b/README.md index fdc61378c..96ca08d11 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,12 @@ Install [osxcross](https://github.com/tpoechtrager/osxcross), then add `-DCMAKE_OSX_SYSROOT=/path/to/osxcross/target/SDK/MacOSX10.11.sdk/` to the **initial** CMake command line. +Install `dmg` and `hfsplus` from +[libdmg-hfsplus](https://github.com/mozilla/libdmg-hfsplus) and `newfs_hfs` +from +[diskdev_cmds](http://pkgs.fedoraproject.org/repo/pkgs/hfsplus-tools/diskdev_cmds-540.1.linux3.tar.gz/0435afc389b919027b69616ad1b05709/diskdev_cmds-540.1.linux3.tar.gz) +to unlock the `package_dmg` target that outputs a macOS disk image. + Importing the official DDNet Database ------------------------------------- diff --git a/cmake/toolchains/darwin.toolchain b/cmake/toolchains/darwin.toolchain index 33e9b0ef0..94ee94074 100644 --- a/cmake/toolchains/darwin.toolchain +++ b/cmake/toolchains/darwin.toolchain @@ -2,6 +2,7 @@ set(CMAKE_SYSTEM_NAME Darwin) set(CMAKE_C_COMPILER o64-clang) set(CMAKE_CXX_COMPILER o64-clang++) +set(CMAKE_INSTALL_NAME_TOOL x86_64-apple-darwin15-install_name_tool) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) diff --git a/other/bundle/client/Info.plist.in b/other/bundle/client/Info.plist.in new file mode 100644 index 000000000..3346bf5e6 --- /dev/null +++ b/other/bundle/client/Info.plist.in @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${TARGET_CLIENT} + CFBundleIconFile + ${TARGET_CLIENT} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + ${PROJECT_VERSION} + CFBundleIdentifier + org.DDNetClient.app + NSHighResolutionCapable + + + diff --git a/other/bundle/client/PkgInfo b/other/bundle/client/PkgInfo new file mode 100644 index 000000000..6f749b0f3 --- /dev/null +++ b/other/bundle/client/PkgInfo @@ -0,0 +1 @@ +APPL???? diff --git a/other/bundle/server/Info.plist.in b/other/bundle/server/Info.plist.in new file mode 100644 index 000000000..6cdd38f0d --- /dev/null +++ b/other/bundle/server/Info.plist.in @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${TARGET_SERVER_LAUNCHER} + CFBundleIconFile + ${TARGET_SERVER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + ${PROJECT_VERSION} + + diff --git a/other/bundle/server/PkgInfo b/other/bundle/server/PkgInfo new file mode 100644 index 000000000..6f749b0f3 --- /dev/null +++ b/other/bundle/server/PkgInfo @@ -0,0 +1 @@ +APPL???? diff --git a/scripts/dmg.py b/scripts/dmg.py new file mode 100644 index 000000000..8f2d4cfc1 --- /dev/null +++ b/scripts/dmg.py @@ -0,0 +1,106 @@ +from collections import namedtuple +import os +import shlex +import subprocess +import tempfile + +ConfigDmgtools = namedtuple('Config', 'dmg hfsplus newfs_hfs verbose') +ConfigHdiutil = namedtuple('Config', 'hdiutil verbose') + +def chunks(l, n): + """ + Yield successive n-sized chunks from l. + + From https://stackoverflow.com/a/312464. + """ + for i in range(0, len(l), n): + yield l[i:i + n] + +class Dmg: + def __init__(self, config): + self.config = config + + def _check_call(self, process_args, *args, **kwargs): + if self.config.verbose >= 1: + print("EXECUTING {}".format(" ".join(shlex.quote(x) for x in process_args))) + if not (self.config.verbose >= 2 and "stdout" not in kwargs): + kwargs["stdout"] = open(os.devnull, 'wb') + subprocess.check_call(process_args, *args, **kwargs) + +class Dmgtools(Dmg): + def _mkfs_hfs(self, *args): + self._check_call((self.config.newfs_hfs,) + args) + def _hfs(self, *args): + self._check_call((self.config.hfsplus,) + args) + def _dmg(self, *args): + self._check_call((self.config.dmg,) + args) + + def _create_hfs(self, hfs, volume_name, size): + if self.config.verbose >= 1: + print("TRUNCATING {} to {} bytes".format(hfs, size)) + with open(hfs, 'wb') as f: + f.truncate(size) + self._mkfs_hfs('-v', volume_name, hfs) + + def _symlink(self, hfs, target, link_name): + self._hfs(hfs, 'symlink', link_name, target) + + def _add(self, hfs, directory): + self._hfs(hfs, 'addall', directory) + + def _finish(self, hfs, dmg): + self._dmg('build', hfs, dmg) + + def create(self, dmg, volume_name, directory, symlinks): + input_size = sum(os.stat(os.path.join(path, f)).st_size for path, dirs, files in os.walk(directory) for f in files) + output_size = max(input_size * 2, 1024**2) + hfs = tempfile.mktemp(prefix=dmg + '.', suffix='.hfs') + self._create_hfs(hfs, volume_name, output_size) + self._add(hfs, directory) + for target, link_name in symlinks: + self._symlink(hfs, target, link_name) + self._finish(hfs, dmg) + if self.config.verbose >= 1: + print("REMOVING {}".format(hfs)) + os.remove(hfs) + +class Hdiutil(Dmg): + def _hdiutil(self, *args): + self._check_call((self.config.hdiutil,) + args) + + def create(self, dmg, volume_name, directory, symlinks): + if symlinks: + raise NotImplementedError("symlinks are not yet implemented") + self._hdiutil('create', '-volname', volume_name, '-srcdir', directory, dmg) + +def main(): + import argparse + p = argparse.ArgumentParser(description="Manipulate dmg archives") + + subcommands = p.add_subparsers(help="Subcommand", dest='command', metavar="COMMAND") + subcommands.required = True + + create = subcommands.add_parser("create", help="Create a dmg archive from files or directories") + create.add_argument('-v', '--verbose', action='count', help="Verbose output") + createx = create.add_mutually_exclusive_group(required=True) + createx.add_argument('--dmgtools', nargs=3, help="Paths to the dmg and hfsplus executable (https://github.com/mozilla/libdmg-hfsplus) and the newfs_hfs executable (http://pkgs.fedoraproject.org/repo/pkgs/hfsplus-tools/diskdev_cmds-540.1.linux3.tar.gz/0435afc389b919027b69616ad1b05709/diskdev_cmds-540.1.linux3.tar.gz)") + createx.add_argument('--hdiutil', help="Path to the hdiutil (only exists for macOS at time of writing)") + create.add_argument('output', metavar="OUTPUT", help="Filename of the output dmg archive") + create.add_argument('volume_name', metavar="VOLUME_NAME", help="Name of the dmg archive") + create.add_argument('directory', metavar="DIR", help="Directory to create the archive from") + create.add_argument('--symlink', metavar="SYMLINK", nargs=2, action="append", help="Symlink the first argument under the second name") + args = p.parse_args() + + verbose = args.verbose or 0 + symlinks = args.symlink or [] + if args.dmgtools: + dmg, hfsplus, newfs_hfs = args.dmgtools + dmg = Dmgtools(ConfigDmgtools(dmg=dmg, hfsplus=hfsplus, newfs_hfs=newfs_hfs, verbose=verbose)) + elif args.hdiutil: + dmg = Hdiutil(ConfigHdiutil(hdiutil=args.hdiutil, verbose=verbose)) + else: + raise RuntimeError("unreachable") + dmg.create(volume_name=args.volume_name, directory=args.directory, dmg=args.output, symlinks=symlinks) + +if __name__ == '__main__': + main() diff --git a/src/osxlaunch/server.m b/src/osxlaunch/server.m deleted file mode 100644 index e542ff16f..000000000 --- a/src/osxlaunch/server.m +++ /dev/null @@ -1,112 +0,0 @@ -#import - -@interface ServerView : NSTextView -{ - NSTask *task; - NSFileHandle *file; -} -- (void)listenTo: (NSTask*)t; -@end - -@implementation ServerView -- (void)listenTo: (NSTask*)t; -{ - NSPipe *pipe; - task = t; - pipe = [NSPipe pipe]; - [task setStandardOutput: pipe]; - file = [pipe fileHandleForReading]; - - [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(outputNotification:) name: NSFileHandleReadCompletionNotification object: file]; - - [file readInBackgroundAndNotify]; -} - -- (void) outputNotification: (NSNotification *) notification -{ - NSData *data = [[[notification userInfo] objectForKey: NSFileHandleNotificationDataItem] retain]; - NSString *string = [[NSString alloc] initWithData: data encoding: NSASCIIStringEncoding]; - NSAttributedString *attrstr = [[NSAttributedString alloc] initWithString: string]; - - [[self textStorage] appendAttributedString: attrstr]; - int length = [[self textStorage] length]; - NSRange range = NSMakeRange(length, 0); - [self scrollRangeToVisible: range]; - - [attrstr release]; - [string release]; - [file readInBackgroundAndNotify]; -} - --(void)windowWillClose:(NSNotification *)notification -{ - [task terminate]; - [NSApp terminate:self]; -} -@end - -void runServer() -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSApp = [NSApplication sharedApplication]; - NSBundle* mainBundle = [NSBundle mainBundle]; - NSTask *task; - task = [[NSTask alloc] init]; - [task setCurrentDirectoryPath: [mainBundle resourcePath]]; - - // get a server config - NSOpenPanel* openDlg = [NSOpenPanel openPanel]; - [openDlg setCanChooseFiles:YES]; - - if([openDlg runModalForDirectory:nil file:nil] != NSOKButton) - return; - - NSArray* filenames = [openDlg filenames]; - if([filenames count] != 1) - return; - - NSString* filename = [filenames objectAtIndex: 0]; - NSArray* arguments = [NSArray arrayWithObjects: @"-f", filename, nil]; - - // run server - NSWindow *window; - ServerView *view; - NSRect graphicsRect; - - graphicsRect = NSMakeRect(100.0, 1000.0, 600.0, 400.0); - - window = [[NSWindow alloc] - initWithContentRect: graphicsRect - styleMask: NSTitledWindowMask - | NSClosableWindowMask - | NSMiniaturizableWindowMask - backing: NSBackingStoreBuffered - defer: NO]; - - [window setTitle: @"DDNet Server"]; - - view = [[[ServerView alloc] initWithFrame: graphicsRect] autorelease]; - [view setEditable: NO]; - [view setRulerVisible: YES]; - - [window setContentView: view]; - [window setDelegate: view]; - [window makeKeyAndOrderFront: nil]; - - [view listenTo: task]; - [task setLaunchPath: [mainBundle pathForAuxiliaryExecutable: @"DDNet-Server"]]; - [task setArguments: arguments]; - [task launch]; - [NSApp run]; - [task terminate]; - - [NSApp release]; - [pool release]; -} - -int main (int argc, char **argv) -{ - runServer(); - - return 0; -} diff --git a/src/osxlaunch/server.m b/src/osxlaunch/server.m new file mode 120000 index 000000000..9ff19d9b5 --- /dev/null +++ b/src/osxlaunch/server.m @@ -0,0 +1 @@ +server.mm \ No newline at end of file diff --git a/src/osxlaunch/server.mm b/src/osxlaunch/server.mm new file mode 100644 index 000000000..97c8301f9 --- /dev/null +++ b/src/osxlaunch/server.mm @@ -0,0 +1,108 @@ +#import + +@interface ServerView : NSTextView +{ + NSTask *task; + NSFileHandle *file; +} +- (void)listenTo: (NSTask *)t; +@end + +@implementation ServerView +- (void)listenTo: (NSTask *)t +{ + NSPipe *pipe; + task = t; + pipe = [NSPipe pipe]; + [task setStandardOutput: pipe]; + file = [pipe fileHandleForReading]; + + [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(outputNotification:) name: NSFileHandleReadCompletionNotification object: file]; + + [file readInBackgroundAndNotify]; +} + +- (void) outputNotification: (NSNotification *) notification +{ + NSData *data = [[[notification userInfo] objectForKey: NSFileHandleNotificationDataItem] retain]; + NSString *string = [[NSString alloc] initWithData: data encoding: NSASCIIStringEncoding]; + NSAttributedString *attrstr = [[NSAttributedString alloc] initWithString: string]; + + [[self textStorage] appendAttributedString: attrstr]; + int length = [[self textStorage] length]; + NSRange range = NSMakeRange(length, 0); + [self scrollRangeToVisible: range]; + + [attrstr release]; + [string release]; + [file readInBackgroundAndNotify]; +} + +-(void)windowWillClose:(NSNotification *)notification +{ + [task terminate]; + [NSApp terminate:self]; +} +@end + +void runServer() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSApp = [NSApplication sharedApplication]; + NSBundle *mainBundle = [NSBundle mainBundle]; + NSTask *task; + task = [[NSTask alloc] init]; + [task setCurrentDirectoryPath: [mainBundle resourcePath]]; + + // get a server config + NSOpenPanel *openDlg = [NSOpenPanel openPanel]; + [openDlg setCanChooseFiles:YES]; + + if([openDlg runModal] != NSOKButton) + return; + + NSString *filename = [[openDlg URL] path]; + NSArray *arguments = [NSArray arrayWithObjects: @"-f", filename, nil]; + + // run server + NSWindow *window; + ServerView *view; + NSRect graphicsRect; + + graphicsRect = NSMakeRect(100.0, 1000.0, 600.0, 400.0); + + window = [[NSWindow alloc] + initWithContentRect: graphicsRect + styleMask: NSTitledWindowMask + | NSClosableWindowMask + | NSMiniaturizableWindowMask + backing: NSBackingStoreBuffered + defer: NO]; + + [window setTitle: @"DDNet Server"]; + + view = [[[ServerView alloc] initWithFrame: graphicsRect] autorelease]; + [view setEditable: NO]; + [view setRulerVisible: YES]; + + [window setContentView: view]; + [window setDelegate: (id)view]; + [window makeKeyAndOrderFront: nil]; + + [view listenTo: task]; + [task setLaunchPath: [mainBundle pathForAuxiliaryExecutable: @"DDNet-Server"]]; + [task setArguments: arguments]; + [task launch]; + [NSApp run]; + [task terminate]; + + [NSApp release]; + [pool release]; +} + +int main(int argc, char **argv) +{ + runServer(); + + return 0; +}