・2016/12/01
Xcodeで Mac OSX用のクリップボード共有&履歴アプリを作る
(Mac OS X用アプリを Swift言語で作る方法、MacOSXでクリップボード監視で変化が有ったら取得)
Tags: [Apple], [MacBook]
● Xcode 7.3.1 Swift 2.2.1で OS X用のアプリを作る
Swiftは Xcode 8の Swift 3になって ++演算子が使えないとか、システムの定数定義のスペルが変わっていたりと大変です。Swift死ねって感じ。
Welcome to Swift.org
ここでは、Xcode 7.3.1 Swift 2.2.1を使用してアプリを作ります。
今まで Windowsで使っていた俺アプリの
・クリップボード履歴アプリの ClipHistW
・クリップボード共有アプリの ClipLanW
を Mac OS X El Capitan 10.11.6に移植します。
●クリップボードの内容を履歴保存する部分
Macの場合はクリップボード オブザーバーの仕組みが無いのでタイマーを使って一定間隔で監視して違っていたら保存と言う実装が慣例となっています。
今回は、タイマーの使い方がまだ解らないのでキーボードフックを行い、Ctrl+Cや Ctrl+Xのキー操作の時にクリップボードからテキスト情報を取り出して保存する様にします。
主な実装処理
・キーボードのグローバルフックでクリップボードの履歴保存
・キーボードのグローバルフックでクリップボードの履歴変更処理
・クリップボードからテキストデータの取得
・クリップボードへテキストデータの設定
・クリップボードテキスト処理時の通知ウィンドウの表示
・UDP受信のスレッド処理
・UDP受信でクリップボード共有テキスト情報の受信
・ステータスバーに常駐化
・メインウィンドウにクリップボード履歴の一覧を表示
・その他、共有通信の設定画面とか
実装として残りの部分は
・UDP通信の受信処理をスレッド化 Macだと Grand Central Dispatch(GCD)化(実装済み)
・ステータスバーに常駐化(実装済み)
・メインウィンドウにクリップボード履歴の一覧を表示
・その他、共有通信の設定画面とか
となります。
●キーボードフック Mac OS X + Xcode 7.3.1 Swift 2.2.1
●キーボード操作のグローバルフック Mac OS X + Xcode 7.3.1 Swift 2.2.1
● UDP送信処理 Mac OS X + Xcode 7.3.1 Swift 2.2.1
● UDP受信処理 Mac OS X + Xcode 7.3.1 Swift 2.2.1
● UDP受信のスレッド処理 Mac OS X + Xcode 7.3.1 Swift 2.2.1
●クリップボードからテキスト情報の取得 Mac OS X + Xcode 7.3.1 Swift 2.2.1
●クリップボードへテキスト情報の設定 Mac OS X + Xcode 7.3.1 Swift 2.2.1
●通知ウィンドウの表示 Mac OS X + Xcode 7.3.1 Swift 2.2.1
●ステータスバーに常駐化 Mac OS X + Xcode 7.3.1 Swift 2.2.1
// Copyright (c) 2016 FREE WING,Y.Sakamoto. All rights reserved.
// スレッド処理の同期用
let semaphore:dispatch_semaphore_t = dispatch_semaphore_create(0)
let queue:dispatch_queue_t = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
// キーボードフックを設定する所
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
NSLog("** applicationDidFinishLaunching")
// 信頼するアプリチェック
let options = NSDictionary(object: kCFBooleanTrue, forKey: kAXTrustedCheckOptionPrompt.takeUnretainedValue() as NSString) as CFDictionaryRef
let trusted = AXIsProcessTrustedWithOptions(options)
// 信頼するアプリチェック
if (trusted) {
// キーボードを押した時のフックを設定
NSEvent.addGlobalMonitorForEventsMatchingMask(.KeyDownMask, handler: self.handlerKeyDownMask)
// キーボードを離した時のフックを設定
NSEvent.addGlobalMonitorForEventsMatchingMask(.KeyUpMask, handler: self.handlerKeyUpMask)
}
// UDP通信の受信処理をスレッド化 Macだと Grand Central Dispatch(GCD)化
dispatch_async(queue) {() -> Void in
// 受信処理
self.receiveUdp()
dispatch_semaphore_signal(semaphore)
}
// ステータスバー常駐設定
initStatusBarMenu()
}
// プログラム終了時
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
// receiveUdp()の終了指示
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
}
// キーボードを押した時に呼ばれるコールバック関数
func handlerKeyUpMask(event: NSEvent!) {
print(event)
if (!event.modifierFlags.contains([.CommandKeyMask])) {
return
}
// Ctrl+X
if event.keyCode == 7 {
// クリップボードの中身を保存する処理
}
// Ctrl+C
if event.keyCode == 8 {
// クリップボードの中身を保存する処理
}
}
// キーボードを離した時に呼ばれるコールバック関数
func handlerKeyDownMask(event: NSEvent!) {
print(event)
// CommandKey + AlternateKey + ShiftKey
if (!event.modifierFlags.contains([.CommandKeyMask, .AlternateKeyMask, .ShiftKeyMask])) {
return
}
// CommandKey + AlternateKey + ShiftKey + '['
// '['
if event.keyCode == 30 {
// クリップボードの履歴ポインタを -1する処理
}
// CommandKey + AlternateKey + ShiftKey + ']'
// ']'
if event.keyCode == 42 {
// クリップボードの履歴ポインタを +1する処理
}
}
// クリップボードからテキストを取り出す処理
func getStringFromPasteboard() -> (Bool, String?) {
let board = NSPasteboard.generalPasteboard()
// クリップボードが空
if board.pasteboardItems?.count == 0 {
return (false, nil);
}
// クリップボードの中身がテキストの時に取得保存
var strArr: Array<String> = []
for item in board.pasteboardItems! {
if let str = item.stringForType("public.utf8-plain-text") {
strArr.append(str)
NSLog(str)
}
}
// テキスト情報が無い
if strArr.count == 0 {
return (false, nil);
}
// クリップボードのテキストを返す
return (true, strArr[0]);
}
// クリップボードにテキストを設定する処理
func setStringToPasteboard(string: String) {
// クリップボードを空にしてから中身を設定する
let board = NSPasteboard.generalPasteboard()
board.clearContents()
let item = NSPasteboardItem()
item.setString(string, forType: NSPasteboardTypeString)
board.writeObjects([item])
}
// ClipHistWのピョコウィンドウの代わり
// 通知を表示するサンプル
{
NSUserNotificationCenter.defaultUserNotificationCenter().delegate = self
let notification = NSUserNotification()
notification.title = "タイトル"
notification.subtitle = "サブタイトル"
notification.informativeText = "テキスト本文"
notification.contentImage = NSImage(named: "blue")
notification.userInfo = ["title" : "タイトル"]
NSUserNotificationCenter.defaultUserNotificationCenter().deliverNotification(notification)
}
// UDP送信のサンプル
func sendUdp() {
let sock: Int32 = socket(AF_INET, SOCK_DGRAM, 0)
var addr = sockaddr_in()
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = UInt16(12345).bigEndian
addr.sin_addr = in_addr()
addr.sin_addr.s_addr = inet_addr("192.168.1.123")
var data: [UInt8] = [0x41, 0x42, 0x43, 0x55, 0x38]
var sendSize : Int = data.count
let r = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendSize, socklen_t(sizeofValue(Int)))
if (r == 0) {
// Err
return
}
withUnsafePointer(&addr) { ptr -> Void in
let addrPtr = UnsafePointer<sockaddr>(ptr)
sendto(sock, &data, Int(data.count), 0, addrPtr, socklen_t(sizeofValue(addr)))
}
close(sock)
}
// UDP受信のサンプル
func receiveUdp() {
let sock: Int32 = socket(AF_INET, SOCK_DGRAM, 0)
var addr = sockaddr_in()
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = UInt16(12345).bigEndian
addr.sin_addr = in_addr()
let status = bindresvport(sock, &addr)
if (status < 0) {
let errMsg = String(UTF8String: strerror(errno))
print(errMsg)
return
}
// Loop
while (true) {
var buffer: [UInt8] = [UInt8](count: 32768, repeatedValue: 0)
let cliaddr = UnsafeMutablePointer<sockaddr_in>.alloc( sizeof(sockaddr_in))
var rlen = socklen_t(sizeof(sockaddr_in))
let n = recvfrom(sock, &buffer, Int(buffer.count), 0, UnsafeMutablePointer<sockaddr>(cliaddr) , &rlen)
if (n < 0) {
let errMsg = String(UTF8String: strerror(errno))
print(errMsg)
return
}
// ClipLanWのパケット構造
var xorKey = buffer[0]
let hiByte = buffer[1]
let loByte = buffer[2]
// let format = buffer[3]
let byteSize: Int = ((numericCast(hiByte) << 8) + numericCast(loByte))
var ptr = UnsafePointer<UInt8>(buffer)
ptr = ptr.advancedBy(4)
// 通信データを復号化する
for i: Int in 0..<byteSize {
let cryptData = buffer[(i+4)]
buffer[i+4] = xorKey ^ cryptData
xorKey = cryptData
}
// バイナリデータを UTF-16の文字列に変換する
let str = String.init(utf16CodeUnits: UnsafePointer<unichar>(ptr), count: (byteSize/2-1))
// 受信したデータをクリップボードに設定する
setStringToPasteboard(str)
}
close(sock)
}
●ステータスバーに常駐化 Mac OS X + Xcode 7.3.1 Swift 2.2.1
/*
Info.plistに下記を設定する(Dockに表示しない。常駐アプリモード・バックグラウンド・アプリケーションモード)
Application is agent (UIElement) = YES
<key>LSUIElement</key>
<string>YES</string>
*/
var statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(NSVariableStatusItemLength)
// ステータスバー常駐設定
func initStatusBarMenu() {
let menu = NSMenu()
// ステータスバーに文字列を表示する場合
// self.statusItem.title = "FREE WING"
// ステータスバーにアイコン画像を表示する場合(x1画像で 16 x 16px)
self.statusItem.image = NSImage(named: "StatusBarIcon");
self.statusItem.highlightMode = true
self.statusItem.menu = menu
let menuItem = NSMenuItem()
menuItem.title = "Exit"
menuItem.action = #selector(AppDelegate.exitApplication(_:))
menu.addItem(menuItem)
}
@IBAction func exitApplication(sender : NSMenuItem) {
// Insert code here to tear down your application
// プログラム終了
NSApplication.sharedApplication().terminate(self)
}
Tags: [Apple], [MacBook]
●関連するコンテンツ(この記事を読んだ人は、次の記事も読んでいます)
Xcode SWIFTで Mac OSX用のスクリーンマスコットアプリを作る
Mac OS X用アプリを Swift言語で作る方法、デスクトップ画面に画像をオーバーレイで描画する方法
Xcode SWIFTで Mac OSX用のスクリーンキャプチャアプリ&画像保存を作る
Mac OS X用アプリを Swift言語で作る方法、デスクトップの画像をキャプチャする方法
Xcode SWIFTで Mac OSX用のスクリーン拡大鏡アプリを作る
Mac OS X用アプリを Swift言語で作る方法、マウスカーソルの位置をリアルタイムで取得する方法
Xcode SWIFTで Mac OSX用の Finderからドラッグ&ドロップを受け付けるアプリを作る
Mac OS X用アプリを Swift言語で作る方法、ドラッグ&ドロップを受け付ける方法
Xcodeで Mac OSX用の使えそうな処理を Tipsとしてまとめ
Mac OS X用アプリを Swift言語で作る方法
Apple MacBook Airを買ってみた。開発者必須アプリの紹介
Xcodeで iPhoneの開発の為に、Mac OS X用アプリを Swift言語で作る方法
Windowsパソコンで Macる。Skylake Platform in OSX86 Hackintoshの夢、BIOS設定内容
Windows PCで iPhone開発、Mac OS Xで Hackintoshの方法、DVMT Pre-Allocated 128MB
VirtualBoxに Mac OS X macOS Sierraをインストールの夢
Windowsの VirtualBoxに Mac環境を作り OS Xをインストールして動かす夢を見る
Raspberry Pi3で iPhoneの開発言語でお馴染みの Swiftを動かし、GPIOを操作して Lチカする方法
Raspbian Jessieで Swift言語のセットアップ方法、GPIOの LEDを Lチカや I2Cを制御する方法
[HOME]
|
[BACK]
リンクフリー(連絡不要、ただしトップページ以外は Web構成の変更で移動する場合があります)
Copyright (c)
2016 FREE WING,Y.Sakamoto
Powered by 猫屋敷工房 & HTML Generator
http://www.neko.ne.jp/~freewing/xcode/xcode_swift_1_clipboard/