qrscanner.py (5178B)
1 #!/usr/bin/env python 2 # 3 # Electrum - lightweight Bitcoin client 4 # Copyright (C) 2015 Thomas Voegtlin 5 # 6 # Permission is hereby granted, free of charge, to any person 7 # obtaining a copy of this software and associated documentation files 8 # (the "Software"), to deal in the Software without restriction, 9 # including without limitation the rights to use, copy, modify, merge, 10 # publish, distribute, sublicense, and/or sell copies of the Software, 11 # and to permit persons to whom the Software is furnished to do so, 12 # subject to the following conditions: 13 # 14 # The above copyright notice and this permission notice shall be 15 # included in all copies or substantial portions of the Software. 16 # 17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 # SOFTWARE. 25 26 import os 27 import sys 28 import ctypes 29 30 from .util import UserFacingException 31 from .i18n import _ 32 from .logging import get_logger 33 34 35 _logger = get_logger(__name__) 36 37 38 if sys.platform == 'darwin': 39 name = 'libzbar.dylib' 40 elif sys.platform in ('windows', 'win32'): 41 name = 'libzbar-0.dll' 42 else: 43 name = 'libzbar.so.0' 44 45 try: 46 libzbar = ctypes.cdll.LoadLibrary(os.path.join(os.path.dirname(__file__), name)) 47 except BaseException as e1: 48 try: 49 libzbar = ctypes.cdll.LoadLibrary(name) 50 except BaseException as e2: 51 libzbar = None 52 if sys.platform != 'darwin': 53 _logger.error(f"failed to load zbar. exceptions: {[e1,e2]!r}") 54 55 56 def scan_barcode_ctypes(device='', timeout=-1, display=True, threaded=False): 57 if libzbar is None: 58 raise UserFacingException("Cannot start QR scanner: zbar not available.") 59 libzbar.zbar_symbol_get_data.restype = ctypes.c_char_p 60 libzbar.zbar_processor_create.restype = ctypes.POINTER(ctypes.c_int) 61 libzbar.zbar_processor_get_results.restype = ctypes.POINTER(ctypes.c_int) 62 libzbar.zbar_symbol_set_first_symbol.restype = ctypes.POINTER(ctypes.c_int) 63 # libzbar.zbar_set_verbosity(100) # verbose logs for debugging 64 proc = libzbar.zbar_processor_create(threaded) 65 libzbar.zbar_processor_request_size(proc, 640, 480) 66 if libzbar.zbar_processor_init(proc, device.encode('utf-8'), display) != 0: 67 raise UserFacingException( 68 _("Cannot start QR scanner: initialization failed.") + "\n" + 69 _("Make sure you have a camera connected and enabled.")) 70 libzbar.zbar_processor_set_visible(proc) 71 if libzbar.zbar_process_one(proc, timeout): 72 symbols = libzbar.zbar_processor_get_results(proc) 73 else: 74 symbols = None 75 libzbar.zbar_processor_destroy(proc) 76 if symbols is None: 77 return 78 if not libzbar.zbar_symbol_set_get_size(symbols): 79 return 80 symbol = libzbar.zbar_symbol_set_first_symbol(symbols) 81 data = libzbar.zbar_symbol_get_data(symbol) 82 return data.decode('utf8') 83 84 def scan_barcode_osx(*args_ignored, **kwargs_ignored): 85 import subprocess 86 # NOTE: This code needs to be modified if the positions of this file changes with respect to the helper app! 87 # This assumes the built macOS .app bundle which ends up putting the helper app in 88 # .app/contrib/osx/CalinsQRReader/build/Release/CalinsQRReader.app. 89 root_ec_dir = os.path.abspath(os.path.dirname(__file__) + "/../") 90 prog = root_ec_dir + "/" + "contrib/osx/CalinsQRReader/build/Release/CalinsQRReader.app/Contents/MacOS/CalinsQRReader" 91 if not os.path.exists(prog): 92 raise UserFacingException("Cannot start QR scanner: helper app not found.") 93 data = '' 94 try: 95 # This will run the "CalinsQRReader" helper app (which also gets bundled with the built .app) 96 # Just like the zbar implementation -- the main app will hang until the QR window returns a QR code 97 # (or is closed). Communication with the subprocess is done via stdout. 98 # See contrib/CalinsQRReader for the helper app source code. 99 with subprocess.Popen([prog], stdout=subprocess.PIPE) as p: 100 data = p.stdout.read().decode('utf-8').strip() 101 return data 102 except OSError as e: 103 raise UserFacingException("Cannot start camera helper app: {}".format(e.strerror)) 104 105 scan_barcode = scan_barcode_osx if sys.platform == 'darwin' else scan_barcode_ctypes 106 107 def _find_system_cameras(): 108 device_root = "/sys/class/video4linux" 109 devices = {} # Name -> device 110 if os.path.exists(device_root): 111 for device in os.listdir(device_root): 112 path = os.path.join(device_root, device, 'name') 113 try: 114 with open(path, encoding='utf-8') as f: 115 name = f.read() 116 except Exception: 117 continue 118 name = name.strip('\n') 119 devices[name] = os.path.join("/dev", device) 120 return devices 121 122 123 if __name__ == "__main__": 124 print(scan_barcode())