From 32773a08eb11d286682ca8cad41171ce804f1631 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Wed, 31 May 2023 15:39:59 +0200 Subject: Works --- CCCB Display.xcodeproj/project.pbxproj | 14 +- .../xcschemes/xcschememanagement.plist | 8 + CCCB Display/AppDelegate.swift | 1 + .../AppIcon.appiconset/Contents.json | 1 + .../AppIcon.appiconset/IMG_4898.jpeg | Bin 0 -> 534617 bytes .../IMG_4898.imageset/Contents.json | 21 ++ .../IMG_4898.imageset/IMG_4898.jpeg | Bin 0 -> 534617 bytes CCCB Display/Base.lproj/LaunchScreen.storyboard | 29 +- CCCB Display/Base.lproj/Main.storyboard | 129 +++++++- CCCB Display/ConfigController.swift | 35 +++ CCCB Display/ViewController.swift | 336 ++++++++++++++++++++- Makefile | 12 + main.c | 70 +++++ 13 files changed, 639 insertions(+), 17 deletions(-) create mode 100644 CCCB Display/Assets.xcassets/AppIcon.appiconset/IMG_4898.jpeg create mode 100644 CCCB Display/Assets.xcassets/IMG_4898.imageset/Contents.json create mode 100644 CCCB Display/Assets.xcassets/IMG_4898.imageset/IMG_4898.jpeg create mode 100644 CCCB Display/ConfigController.swift create mode 100644 Makefile create mode 100644 main.c diff --git a/CCCB Display.xcodeproj/project.pbxproj b/CCCB Display.xcodeproj/project.pbxproj index e81c1d3..76c1843 100644 --- a/CCCB Display.xcodeproj/project.pbxproj +++ b/CCCB Display.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 65357C6D2A2013490096BC93 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 65357C6B2A2013490096BC93 /* Main.storyboard */; }; 65357C6F2A20134C0096BC93 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 65357C6E2A20134C0096BC93 /* Assets.xcassets */; }; 65357C722A20134C0096BC93 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 65357C702A20134C0096BC93 /* LaunchScreen.storyboard */; }; + 65357C7A2A2105100096BC93 /* ConfigController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65357C792A2105100096BC93 /* ConfigController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -24,6 +25,7 @@ 65357C6E2A20134C0096BC93 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 65357C712A20134C0096BC93 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 65357C732A20134C0096BC93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 65357C792A2105100096BC93 /* ConfigController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -57,6 +59,7 @@ isa = PBXGroup; children = ( 65357C652A2013490096BC93 /* AppDelegate.swift */, + 65357C792A2105100096BC93 /* ConfigController.swift */, 65357C672A2013490096BC93 /* SceneDelegate.swift */, 65357C692A2013490096BC93 /* ViewController.swift */, 65357C6B2A2013490096BC93 /* Main.storyboard */, @@ -141,6 +144,7 @@ 65357C6A2A2013490096BC93 /* ViewController.swift in Sources */, 65357C662A2013490096BC93 /* AppDelegate.swift in Sources */, 65357C682A2013490096BC93 /* SceneDelegate.swift in Sources */, + 65357C7A2A2105100096BC93 /* ConfigController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -287,13 +291,14 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 8DUP9J636N; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "CCCB Display/Info.plist"; + INFOPLIST_KEY_NSCameraUsageDescription = "This app accesses the camera to display a monochrome representation on the CCCB Display"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -314,13 +319,14 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 8DUP9J636N; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "CCCB Display/Info.plist"; + INFOPLIST_KEY_NSCameraUsageDescription = "This app accesses the camera to display a monochrome representation on the CCCB Display"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/CCCB Display.xcodeproj/xcuserdata/erdgeist.xcuserdatad/xcschemes/xcschememanagement.plist b/CCCB Display.xcodeproj/xcuserdata/erdgeist.xcuserdatad/xcschemes/xcschememanagement.plist index f15d7db..a661d77 100644 --- a/CCCB Display.xcodeproj/xcuserdata/erdgeist.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/CCCB Display.xcodeproj/xcuserdata/erdgeist.xcuserdatad/xcschemes/xcschememanagement.plist @@ -10,5 +10,13 @@ 0 + SuppressBuildableAutocreation + + 65357C612A2013490096BC93 + + primary + + + diff --git a/CCCB Display/AppDelegate.swift b/CCCB Display/AppDelegate.swift index 8639c64..a3e61dc 100644 --- a/CCCB Display/AppDelegate.swift +++ b/CCCB Display/AppDelegate.swift @@ -14,6 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. + UIApplication.shared.isIdleTimerDisabled = true return true } diff --git a/CCCB Display/Assets.xcassets/AppIcon.appiconset/Contents.json b/CCCB Display/Assets.xcassets/AppIcon.appiconset/Contents.json index 13613e3..bfe77c1 100644 --- a/CCCB Display/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/CCCB Display/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,6 +1,7 @@ { "images" : [ { + "filename" : "IMG_4898.jpeg", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/CCCB Display/Assets.xcassets/AppIcon.appiconset/IMG_4898.jpeg b/CCCB Display/Assets.xcassets/AppIcon.appiconset/IMG_4898.jpeg new file mode 100644 index 0000000..b9f2603 Binary files /dev/null and b/CCCB Display/Assets.xcassets/AppIcon.appiconset/IMG_4898.jpeg differ diff --git a/CCCB Display/Assets.xcassets/IMG_4898.imageset/Contents.json b/CCCB Display/Assets.xcassets/IMG_4898.imageset/Contents.json new file mode 100644 index 0000000..ce6efb2 --- /dev/null +++ b/CCCB Display/Assets.xcassets/IMG_4898.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "IMG_4898.jpeg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CCCB Display/Assets.xcassets/IMG_4898.imageset/IMG_4898.jpeg b/CCCB Display/Assets.xcassets/IMG_4898.imageset/IMG_4898.jpeg new file mode 100644 index 0000000..b9f2603 Binary files /dev/null and b/CCCB Display/Assets.xcassets/IMG_4898.imageset/IMG_4898.jpeg differ diff --git a/CCCB Display/Base.lproj/LaunchScreen.storyboard b/CCCB Display/Base.lproj/LaunchScreen.storyboard index 865e932..37c47c1 100644 --- a/CCCB Display/Base.lproj/LaunchScreen.storyboard +++ b/CCCB Display/Base.lproj/LaunchScreen.storyboard @@ -1,8 +1,10 @@ - - + + + - + + @@ -11,10 +13,21 @@ - + - + + + + + + + + + + + + @@ -22,4 +35,10 @@ + + + + + + diff --git a/CCCB Display/Base.lproj/Main.storyboard b/CCCB Display/Base.lproj/Main.storyboard index 25a7638..247b634 100644 --- a/CCCB Display/Base.lproj/Main.storyboard +++ b/CCCB Display/Base.lproj/Main.storyboard @@ -1,24 +1,143 @@ - + + - + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CCCB Display/ConfigController.swift b/CCCB Display/ConfigController.swift new file mode 100644 index 0000000..92b6e37 --- /dev/null +++ b/CCCB Display/ConfigController.swift @@ -0,0 +1,35 @@ +// +// ConfigController.swift +// CCCB Display +// +// Created by Dirk Engling on 26.05.23. +// + +import Foundation +import UIKit + + +class ConfigController: UIViewController { + + @IBOutlet weak var display_address: UITextField! + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + let defaults = UserDefaults.standard + if let ip = defaults.string(forKey: "Display_Address") { + display_address.text = ip + } else { + display_address.text = "172.23.42.29" + } + } + + @IBAction func cancelPressed(_ sender: Any) { + self.dismiss(animated: true) + } + + @IBAction func donePressed(_ sender: Any) { + self.dismiss(animated: true) + let defaults = UserDefaults.standard + defaults.set(display_address.text, forKey: "Display_Address") + } +} diff --git a/CCCB Display/ViewController.swift b/CCCB Display/ViewController.swift index 53f3cee..d0f8dbe 100644 --- a/CCCB Display/ViewController.swift +++ b/CCCB Display/ViewController.swift @@ -6,14 +6,344 @@ // import UIKit +import AVFoundation +import CoreImage +import Network -class ViewController: UIViewController { +class ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate, ObservableObject, AVCaptureVideoDataOutputSampleBufferDelegate { + + @IBOutlet weak var frameRateLabel: UILabel! + @IBOutlet weak var cameraView: UIView! + + var device: AVCaptureDevice? + var input: AVCaptureDeviceInput? + var prevLayer: AVCaptureVideoPreviewLayer? + + private let captureSession = AVCaptureSession() + private let videoDataOutput = AVCaptureVideoDataOutput() + private let sessionQueue = DispatchQueue(label: "sessionQueue") + private let context = CIContext() + +// var hostUDP: NWEndpoint.Host = "172.23.42.29" + var hostUDP: NWEndpoint.Host? + var portUDP: NWEndpoint.Port = 2342 + var connectionUDP: NWConnection? + + var lastTimeStamp: CFTimeInterval = CACurrentMediaTime() + + /* Physical Display control packet parameters: */ + private let HEADERLEN = 10 + private let WIDTH = 448 + private let HEIGHT = 160 + private let VHEIGHT = 236 override func viewDidLoad() { super.viewDidLoad() - // Do any additional setup after loading the view. + + UserDefaults.standard.addObserver(self, forKeyPath: "Display_Address", options: .new, context: nil) + constructSocket() + + switch AVCaptureDevice.authorizationStatus(for: .video) { + case .authorized: // the user has already authorized to access the camera. + DispatchQueue.main.async { + self.createSession() + } + case .notDetermined: + AVCaptureDevice.requestAccess (for: .video) { (granted) in + if granted { + print("the user has granted to access the camera") + DispatchQueue.main.async { + self.createSession () + } + } else { + print("the user has not granted to access the camera") + let dialogMessage = UIAlertController(title: "Attention", message: "Can not work without camera access", preferredStyle: .alert) + self.present(dialogMessage, animated: true, completion: nil) + } + } + case .denied: + print("the user has denied previously to access the camera.") + let dialogMessage = UIAlertController(title: "Attention", message: "Can not work without camera access", preferredStyle: .alert) + self.present(dialogMessage, animated: true, completion: nil) + + case .restricted: + print("the user can't give camera access due to some restriction.") + let dialogMessage = UIAlertController(title: "Attention", message: "Can not work without camera access", preferredStyle: .alert) + self.present(dialogMessage, animated: true, completion: nil) + + default: + print("something has wrong due to we can't access the camera.") + let dialogMessage = UIAlertController(title: "Attention", message: "Can not work without camera access", preferredStyle: .alert) + self.present(dialogMessage, animated: true, completion: nil) + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + prevLayer?.frame.size = cameraView.frame.size } + func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { -} + let now = CACurrentMediaTime() + let freq = (Int)(1 / (now - lastTimeStamp)) +// print ("Elapsed: \(now - lastTimeStamp) - Frequency: \(1 / (now - lastTimeStamp))") + lastTimeStamp = now + + guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } + + CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly) + let bufferWidth = CVPixelBufferGetWidth(pixelBuffer) + let bufferHeight = CVPixelBufferGetHeight(pixelBuffer) + let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer) + let kBytesPerPixel = 4 + + print("\(bufferWidth) \(bufferHeight) \(bytesPerRow)") + guard let baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer) else { return } + + var packet: [UInt8] = [0, 0x12, 0, 0, 0x23, 0, 0, 0, 0, 0] + var scratch: [Int] = Array(repeating: 0, count: WIDTH*(2+VHEIGHT)) + + let t1 = CACurrentMediaTime() + + // 160 real rows are interleaved with 19 gaps of 4 pixels height on the display + // so we create 20 virtual blocks of 8 real and 4 virtual pixels + // we overlay VHEIGHT==236 virtual rows on the image and later skip the 4 invisble rows + var off = 0 + for row in 0..> 23) + accv += 1 + if accv == 8 { + packet.append((UInt8)(acc)) + acc = 0 + accv = 0 + } + } + + let err = (pixel - bwpixel) / 42 + + func AddSatShift(_ scr: inout Array, _ X: Int, _ Y: Int, _ SHIFT: Int) { + let inner_p = (row + Y) * WIDTH + column + X + var r = scr[inner_p] + (err << (16 - SHIFT)) + if r < 0 { + r = 0 + } + if r > 0xffffff { + r = 0xffffff + } + scr[inner_p] = r + } + + AddSatShift(&scratch, 0, 1, 13) + AddSatShift(&scratch, 0, 2, 14) + if (column > 0) { + AddSatShift(&scratch, -1, 1, 14) + AddSatShift(&scratch, -1, 2, 15) + } + + if (column > 1) { + AddSatShift(&scratch, -2, 1, 15) + AddSatShift(&scratch, -2, 2, 16) + } + + if (column < WIDTH - 1) { + AddSatShift(&scratch, 1, 0, 13) + AddSatShift(&scratch, 1, 1, 14) + AddSatShift(&scratch, 1, 2, 15) + } + + if (column < WIDTH - 2) { + AddSatShift(&scratch, 2, 0, 14) + AddSatShift(&scratch, 2, 1, 15) + AddSatShift(&scratch, 2, 2, 16) + } + + } + } + + let t2 = CACurrentMediaTime() + +// print("dur \(t2 - t1)") + DispatchQueue.main.async { + self.frameRateLabel.text = String(format: "%.04f (%d Hz)", t2 - t1, freq) + } + + self.connectionUDP?.send(content: packet, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in + if (NWError == nil) { + print("Data was sent to UDP") + } else { + print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)") + self.constructSocket() + } + }))) + } + func createSession() { + guard let device = AVCaptureDevice.default(for: AVMediaType.video) else { return } + do { + input = try AVCaptureDeviceInput(device: device) + } + catch { + print(error) + } + + captureSession.sessionPreset = AVCaptureSession.Preset.vga640x480 + if let input = input { + captureSession.addInput(input) + } + + prevLayer = AVCaptureVideoPreviewLayer(session: captureSession) + prevLayer?.frame.size = cameraView.frame.size + prevLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill + + cameraView.layer.addSublayer(prevLayer!) + captureSession.addOutput(videoDataOutput) + captureSession.commitConfiguration() + + videoDataOutput.videoSettings.updateValue(kCVPixelFormatType_32BGRA, forKey: "PixelFormatType") + videoDataOutput.setSampleBufferDelegate(self, queue: self.sessionQueue) + + do { + try device.lockForConfiguration() + device.activeVideoMaxFrameDuration = CMTimeMake(value: 1, timescale: 60) + device.activeVideoMinFrameDuration = CMTimeMake(value: 1, timescale: 60) + device.unlockForConfiguration() + } catch { + print(error) + } + + let captureConnection = videoDataOutput.connection(with: .video) + captureConnection?.isEnabled = true + deviceOrientationDidChange(Notification(name: UIDevice.orientationDidChangeNotification)) + // captureConnection?.videoOrientation = .landscapeRight + + sessionQueue.async { self.captureSession.startRunning() } + } + + func cameraWithPosition(position: AVCaptureDevice.Position) -> AVCaptureDevice? { + if #available(iOS 11.1, *) { + let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera, .builtInTelephotoCamera, .builtInTrueDepthCamera, .builtInWideAngleCamera, ], mediaType: .video, position: position) + + if let device = deviceDiscoverySession.devices.first { + return device + } + else { + //add code here + } + return nil + } + + return device + } + + func transformOrientation(orientation: UIInterfaceOrientation) -> AVCaptureVideoOrientation { + switch orientation { + case .landscapeLeft: + return .landscapeLeft + case .landscapeRight: + return .landscapeRight + case .portraitUpsideDown: + return .portraitUpsideDown + default: + return .portrait + } + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "Display_Address" { + constructSocket() + } + } + + func constructSocket() { + let defaults = UserDefaults.standard + if let ip = defaults.string(forKey: "Display_Address") { + hostUDP = NWEndpoint.Host(ip) + } else { + hostUDP = NWEndpoint.Host("172.23.42.29") + // hostUDP = NWEndpoint.Host("84.200.61.9") + // hostUDP = NWEndpoint.Host("192.168.178.69") + } +// hostUDP = NWEndpoint.Host("192.168.178.69") + + self.connectionUDP = NWConnection(host: hostUDP!, port: portUDP, using: .udp) + self.connectionUDP?.start(queue: .global()) + } + + @IBAction func switchCameraSide(sender: AnyObject) { + let currentCameraInput: AVCaptureInput = captureSession.inputs[0] + captureSession.removeInput(currentCameraInput) + var newCamera: AVCaptureDevice + if (currentCameraInput as! AVCaptureDeviceInput).device.position == .back { + newCamera = self.cameraWithPosition(position: .front)! + } else { + newCamera = self.cameraWithPosition(position: .back)! + } + + var newVideoInput: AVCaptureDeviceInput? + do{ + newVideoInput = try AVCaptureDeviceInput(device: newCamera) + } + catch{ + print(error) + } + + if let newVideoInput = newVideoInput{ + captureSession.addInput(newVideoInput) + deviceOrientationDidChange(Notification(name: UIDevice.orientationDidChangeNotification)) + } + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + NotificationCenter.default.addObserver(self, selector: #selector(deviceOrientationDidChange), + name: UIDevice.orientationDidChangeNotification, object: nil) + deviceOrientationDidChange(Notification(name: UIDevice.orientationDidChangeNotification)) + + sessionQueue.async { self.captureSession.startRunning() } + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + sessionQueue.async { self.captureSession.stopRunning() } + NotificationCenter.default.removeObserver(self) + } + + @objc func deviceOrientationDidChange(_ notification: Notification) { + let orientation = UIDevice.current.orientation + let captureConnection = videoDataOutput.connection(with: .video) + + if orientation == .landscapeLeft { + self.prevLayer?.connection?.videoOrientation = .landscapeRight + captureConnection?.videoOrientation = .landscapeRight + } else { + self.prevLayer?.connection?.videoOrientation = .landscapeLeft + captureConnection?.videoOrientation = .landscapeLeft + } + } +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6ec9f52 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +all: main + +SDL_LIBRARIES=/usr/local/lib/libSDL2.a /usr/local/lib/libSDL2main.a /usr/local/lib/libSDL2_gfx.a +SDL_DEPEND_FRAMEWORKS=-framework Carbon -framework Cocoa -framework CoreAudio -framework AudioToolbox -framework CoreVideo -framework ForceFeedback -framework IOKit -framework CoreHaptics -framework GameController -framework Metal +SDL_DEPEND_LIBRARIES=/usr/local/lib/libpng.a /usr/local/lib/libfreetype.a -lbz2 -liconv -lz + +main: main.c + cc -g -O3 -o CCCBDisplay main.c -I /usr/local/include -lm $(SDL_LIBRARIES) $(SDL_DEPEND_FRAMEWORKS) $(SDL_DEPEND_LIBRARIES) + +.PHONY: clean +clean: + rm -f CCCBDisplay diff --git a/main.c b/main.c new file mode 100644 index 0000000..82b3c07 --- /dev/null +++ b/main.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include + +#include "SDL2/SDL.h" +#include "SDL2/SDL2_gfxPrimitives.h" + +int main() { + int sock_in = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + struct sockaddr_in server_addr; + + SDL_Window *screen; + SDL_Renderer *renderer; + int screen_width = 448; + //int screen_height = 236; + int screen_height = 160; + uint8_t inbuf[screen_width * screen_height / 8 + 10]; + + if (SDL_Init(SDL_INIT_EVERYTHING) == -1) { + fprintf(stderr, "Can't initialize SDL.\n"); + exit(1); + } + screen = SDL_CreateWindow("Laserharfe", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_width, screen_height, SDL_WINDOW_OPENGL); + if (!screen) { + fprintf(stderr, "Can't set video mode.\n"); + exit(1); + } + renderer = SDL_CreateRenderer(screen, -1, 0); + + SDL_RenderClear(renderer); + + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(2342); + server_addr.sin_addr.s_addr = inet_addr("192.168.178.69"); + + if (bind(sock_in, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){ + fprintf(stderr, "Couldn't bind to the port\n"); + exit(1); + } + + struct timeval tv; + memset(&tv, 0, sizeof(tv)); + tv.tv_usec = 30000; + setsockopt(sock_in, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + + while (1) { + SDL_Event ev; + SDL_PollEvent(&ev); + if (ev.type == SDL_QUIT) exit(0); + + size_t received = recvfrom(sock_in, inbuf, sizeof(inbuf), 0, NULL, 0); + if (received == -1) + continue; + + printf("Packet received, size: %zd\n", received); + SDL_RenderClear(renderer); + + for (int row = 0; row < screen_height; ++row) { + for (int col = 0; col < screen_width; ++col) { + uint8_t pixel = inbuf[10 + (row * screen_width + col) / 8]; + int on = pixel << (col & 7); + filledCircleColor(renderer, col, row, 1, (on & 0x80) ? 0xffffffff : 0); + } + } + SDL_RenderPresent(renderer); + } +} -- cgit v1.2.3