通过 SSH 登录后是否可以访问 Mac OS X 粘贴板?

发布于 2024-08-01 15:40:31 字数 597 浏览 4 评论 0 原文

我们有以下片段。

OSStatus createErr = PasteboardCreate(kPasteboardClipboard, &m_pboard);
if (createErr != noErr) {
    LOG((CLOG_DEBUG "failed to create clipboard reference: error %i" createErr));
}

这编译得很好,但是,从 SSH 调用时它无法运行。 这是因为 SSH 终端中没有可用的粘贴板。 然而,这里的想法是在计算机之间共享剪贴板。

从桌面终端运行时,效果很好。 但是当从 SSH 运行时,PasteboardCreate 返回 -4960(又名 coreFoundationUnknownErr)。 我认为解决此问题的唯一方法是在与粘贴板相同的环境中运行应用程序,但这可能吗?

We have the following snippet.

OSStatus createErr = PasteboardCreate(kPasteboardClipboard, &m_pboard);
if (createErr != noErr) {
    LOG((CLOG_DEBUG "failed to create clipboard reference: error %i" createErr));
}

This compiles fine, however, it fails to run when called from SSH. This is because there is no pasteboard available in the SSH terminal. However, the idea here is to share clipboards between computers.

When run from desktop terminal, this works just fine. But when run from SSH, PasteboardCreate returns -4960 (aka, coreFoundationUnknownErr). I assume that the only way around this issue is to run the application from within the same environment as the pasteboard, but is this possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

羁拥 2024-08-08 15:40:31

我尝试在 AppleScript 中执行此操作,并且它有效(即使通过 SSH 调用)。 我的脚本如下:

#!/usr/bin/osascript

on run
    tell application "Finder"
        display dialog (get the clipboard)
    end tell
end run

这绝对不是一个理想的解决方案,但也许如果您弄清楚 AppleScript 是如何做到这一点的,那么它会帮助您自己实现它。

I tried doing it in AppleScript, and it worked (even when invoked via SSH). My script is as follows:

#!/usr/bin/osascript

on run
    tell application "Finder"
        display dialog (get the clipboard)
    end tell
end run

This definitely isn't an ideal solution, but perhaps if you worked out how AppleScript does it then it'd help you implement it yourself.

茶色山野 2024-08-08 15:40:31

您可以在 SnowLeopard 上通过 SSH 使用 PasteboardCreate 访问粘贴板,但在 Leopard 或 Tiger 上则不行。

您可能不想使用 pbcopy 和 pbpaste 进行完整的粘贴板同步,因为它们仅处理纯文本、RTF 和 EPS。 例如,如果您复制图像然后尝试使用 pbpaste 将其写出,您将不会得到任何输出。

假设您有一个应用程序在两台计算机上的用户会话中运行,您可以将粘贴板数据序列化到一个文件中,通过 SSH 传输它,从远程端的应用程序中读取它,然后将粘贴板数据放在远程粘贴板上。 然而,正确地实现粘贴板序列化可能很棘手,而且我不确定操作系统和体系结构之间的可移植性粘贴板数据如何。

You can access the pasteboard with PasteboardCreate via SSH on SnowLeopard but not on Leopard or Tiger.

You probably don't want to use pbcopy and pbpaste for a full pasteboard sync since those only deal with plain text, RTF, and EPS. If, for example, you copy an image and then try to write it out with pbpaste, you'll get no output.

Assuming you have an app running in the user's session on both computers, you could serialize the pasteboard data out to a file, transfer it over SSH, read it from your app on the remote side, and then put the pasteboard data on the remote pasteboard. However, getting the pasteboard serialization right may be tricky and I'm not sure how portable pasteboard data is between OSes and architectures.

当爱已成负担 2024-08-08 15:40:31

直接访问粘贴板似乎是不行的。 首先,启动 不会向粘贴板服务器的 mach 端口注册进程1。 您首先需要找到一种方法来获取粘贴板服务器的马赫端口(mach_port_names?)。 此外,用户会话之间的直接通信是 禁止2,其他通讯受到限制。 我不确定您的程序是否有权连接到粘贴板服务器。

这是使用 Apple 事件获取和获取信息的说明性示例的第一个镜头。 将剪贴板设置为字符串。 错误处理很少甚至不存在(我不确定我对 require_noerr 的感觉如何)。 如果您要在运行期间多次获取/设置剪贴板数据,您可以保存 Apple 事件,并在复制到剪贴板时使用 AECreateDesc & AEPutParamDesc 或(可能)AEBuildParametersAEVTBuilder 可能有用。

NSString* paste() {
    NSString *content;

    AppleEvent paste, reply = { typeNull, 0L };
    AEBuildError buildError = { typeNull, 0L };
    AEDesc clipDesc = { typeNull, 0L };

    OSErr err;

    err = AEBuildAppleEvent(kAEJons, kAEGetClipboard, 
                            typeApplicationBundleID, "com.apple.finder", strlen("com.apple.finder"), 
                            kAutoGenerateReturnID, kAnyTransactionID,
                            &paste, &buildError,
                            ""
        );
    require_noerr(err, paste_end);
    err = AESendMessage(&paste, &reply, kAEWaitReply, kAEDefaultTimeout);
    err = AEGetParamDesc(&reply, keyDirectObject, typeUTF8Text, &clipDesc);
    require_noerr(err, pastErr_getReply);

    Size dataSize = AEGetDescDataSize(&clipDesc);
    char* clipData = malloc(dataSize);
    if (clipData) {
        err = AEGetDescData(&clipDesc, clipData, dataSize);
        if (noErr == err) {
            content = [NSString stringWithCString:clipData encoding:NSUTF8StringEncoding];
        } else {}
        free(clipData);
    }

    AEDisposeDesc(&clipDesc);
pastErr_getReply:
    AEDisposeDesc(&reply);
pasteErr_sending:
    AEDisposeDesc(&paste);
paste_end:
    return content;
}

OSStatus copy(NSString* clip) {
    AppleEvent copy, reply = { typeNull, 0L };
    AEBuildError buildError = { typeNull, 0L };

    OSErr err = AEBuildAppleEvent(kAEJons, kAESetClipboard, 
                                  typeApplicationBundleID, "com.apple.finder", strlen("com.apple.finder"), 
                                  kAutoGenerateReturnID, kAnyTransactionID,
                                  ©, &buildError,
                                  "'----':utf8(@)",
                                  AEPARAMSTR([clip UTF8String])
                                  /*
                                    "'----':obj {form: enum(prop), want: type(@), seld: type(@), from: null()}"
                                    "data:utf8(@)",
                                    AEPARAM(typeUTF8Text),
                                    AEPARAM(pClipboard),
                                    AEPARAMSTR([clip UTF8String])
                                  */
        );
    if (aeBuildSyntaxNoErr != buildError.fError) {
        return err;
    }
    AESendMessage(©, &reply, kAENoReply, kAEDefaultTimeout);
    AEDisposeDesc(&reply);
    AEDisposeDesc(©);
    return noErr;
}

我将保留上面的核心基础方法,但您可能想要使用 NSAppleEventDescriptor 从 Apple 事件回复中提取剪贴板内容。

    err = AESendMessage(&paste, &reply, kAEWaitReply, kAEDefaultTimeout);
require_noerr(err, pasteErr_sending);
    // nsReply takes ownership of reply
    NSAppleEventDescriptor *nsReply = [[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&reply];
    content = [[nsReply descriptorAtIndex:1] stringValue];
    [nsReply release];

pasteErr_sending:
    AEDisposeDesc(&paste);
paste_end:
    return content;
}

在调试器中检查 NSAppleEventDescriptor 也比 AEDesc 更容易。 要检查回复,您还可以在使用 osascript 或 Script Editor.app 时设置 AEDebugReceives 环境变量:

AEDebugReceives=1 osascript -e 'tell application "Finder" to get the clipboard'

参考:

  1. "配置用户会话"
  2. "跨登录会话进行通信"
  3. Mach 内核接口,特别是:
  4. CFMessagePort 参考 (马赫端口包装器):
  5. Apple 事件编程指南
  6. Apple 事件管理器参考
  7. AEBuild*、AEPrint* 和朋友
  8. CocoaDev 上的 AEBuildAppleEvent
  9. Mac OS X 调试魔法(对于 AEDebugSends 和其他 AEDebug* 环境变量)

Accessing the pasteboard directly looks to be a no-go. First, launchd won't register the processes1 with the pasteboard server's mach port. You'd first need find a way to get the pasteboard server's mach port (mach_port_names?). Also, direct communication between user sessions is prohibited2, and other communication is limited. I'm not sure if your program will have the rights to connect to the pasteboard server.

Here's a first shot at an illustrative example on using Apple events to get & set the clipboard as a string. Error handling is minimal to nonexistent (I'm not certain how I feel about require_noerr). If you're going to get/set clipboard data multiple times during a run, you can save the Apple events and, when copying to the clipboard, use AECreateDesc & AEPutParamDesc or (maybe) AEBuildParameters. AEVTBuilder might be of use.

NSString* paste() {
    NSString *content;

    AppleEvent paste, reply = { typeNull, 0L };
    AEBuildError buildError = { typeNull, 0L };
    AEDesc clipDesc = { typeNull, 0L };

    OSErr err;

    err = AEBuildAppleEvent(kAEJons, kAEGetClipboard, 
                            typeApplicationBundleID, "com.apple.finder", strlen("com.apple.finder"), 
                            kAutoGenerateReturnID, kAnyTransactionID,
                            &paste, &buildError,
                            ""
        );
    require_noerr(err, paste_end);
    err = AESendMessage(&paste, &reply, kAEWaitReply, kAEDefaultTimeout);
    err = AEGetParamDesc(&reply, keyDirectObject, typeUTF8Text, &clipDesc);
    require_noerr(err, pastErr_getReply);

    Size dataSize = AEGetDescDataSize(&clipDesc);
    char* clipData = malloc(dataSize);
    if (clipData) {
        err = AEGetDescData(&clipDesc, clipData, dataSize);
        if (noErr == err) {
            content = [NSString stringWithCString:clipData encoding:NSUTF8StringEncoding];
        } else {}
        free(clipData);
    }

    AEDisposeDesc(&clipDesc);
pastErr_getReply:
    AEDisposeDesc(&reply);
pasteErr_sending:
    AEDisposeDesc(&paste);
paste_end:
    return content;
}

OSStatus copy(NSString* clip) {
    AppleEvent copy, reply = { typeNull, 0L };
    AEBuildError buildError = { typeNull, 0L };

    OSErr err = AEBuildAppleEvent(kAEJons, kAESetClipboard, 
                                  typeApplicationBundleID, "com.apple.finder", strlen("com.apple.finder"), 
                                  kAutoGenerateReturnID, kAnyTransactionID,
                                  ©, &buildError,
                                  "'----':utf8(@)",
                                  AEPARAMSTR([clip UTF8String])
                                  /*
                                    "'----':obj {form: enum(prop), want: type(@), seld: type(@), from: null()}"
                                    "data:utf8(@)",
                                    AEPARAM(typeUTF8Text),
                                    AEPARAM(pClipboard),
                                    AEPARAMSTR([clip UTF8String])
                                  */
        );
    if (aeBuildSyntaxNoErr != buildError.fError) {
        return err;
    }
    AESendMessage(©, &reply, kAENoReply, kAEDefaultTimeout);
    AEDisposeDesc(&reply);
    AEDisposeDesc(©);
    return noErr;
}

I'm leaving the Core Foundation approach above, but you'll probably want to use NSAppleEventDescriptor to extract the clipboard contents from the Apple Event reply.

    err = AESendMessage(&paste, &reply, kAEWaitReply, kAEDefaultTimeout);
require_noerr(err, pasteErr_sending);
    // nsReply takes ownership of reply
    NSAppleEventDescriptor *nsReply = [[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&reply];
    content = [[nsReply descriptorAtIndex:1] stringValue];
    [nsReply release];

pasteErr_sending:
    AEDisposeDesc(&paste);
paste_end:
    return content;
}

An NSAppleEventDescriptor is also easier to examine in a debugger than an AEDesc. To examine replies, you can also to set the AEDebugReceives environment variable when using osascript or Script Editor.app:

AEDebugReceives=1 osascript -e 'tell application "Finder" to get the clipboard'

References:

  1. "Configuring User Sessions"
  2. "Communicating Across Login Sessions"
  3. Mach Kernel Interface, especially:
  4. CFMessagePort Reference (mach port wrapper):
  5. Apple Events Programming Guide
  6. Apple Event Manager Reference
  7. AEBuild*, AEPrint* and Friends
  8. AEBuildAppleEvent on CocoaDev
  9. Mac OS X Debugging Magic (for AEDebugSends and other AEDebug* environment variables)
¢好甜 2024-08-08 15:40:31

看一下 pbpaste (获取剪贴板的内容)和 pbcopy (将内容复制到剪贴板)。 工作正常,也可以通过 SSH 进行。 :)

在 Mac OS X Snow Leopard 上:

pbcopy Mac
(来源:hillrippers.ch

在 Ubuntu 9.04 上:

pbpaste Ubuntu
(来源:hillrippers.ch

Take a look at pbpaste (getting the contents of the clipboard) and pbcopy (copying contents TO the clipboard). Works fine, also over SSH. :)

On Mac OS X Snow Leopard:

pbcopy Mac
(source: hillrippers.ch)

On Ubuntu 9.04:

pbpaste Ubuntu
(source: hillrippers.ch)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文