सी नॉन-ब्लॉकिंग कीबोर्ड इनपुट


82

मैं सी (लिनक्स पर) में एक प्रोग्राम लिखने की कोशिश कर रहा हूं जो उपयोगकर्ता को एक कुंजी दबाने तक लूप करता है, लेकिन किसी भी लूप को जारी रखने के लिए कीपर की आवश्यकता नहीं है।

क्या ऐसा करने के लिए एक सरल तरीका है? मुझे select()लगता है कि मैं इसे संभवतः कर सकता हूं लेकिन यह बहुत काम की तरह लगता है।

वैकल्पिक रूप से, क्या गैर-अवरोधक io के बजाय प्रोग्राम बंद होने से पहले ctrl- cसफाई करने के लिए एक - कीपर पकड़ने का एक तरीका है ?


एक धागा खोलने के बारे में क्या?
नादव बी

जवाबों:


65

जैसा कि पहले ही कहा गया है, आप sigactionctrl-c selectको फंसाने के लिए , या किसी भी मानक इनपुट को फंसाने के लिए उपयोग कर सकते हैं ।

ध्यान दें कि बाद की विधि के साथ आपको TTY सेट करने की भी आवश्यकता है ताकि यह लाइन-ऑन-ए-टाइम मोड के बजाय वर्ण-पर-समय में हो। उत्तरार्द्ध डिफ़ॉल्ट है - यदि आप पाठ की एक पंक्ति में टाइप करते हैं, तो यह रनिंग प्रोग्राम के स्टडिन में नहीं भेजा जाता है जब तक कि आप एंटर नहीं करते हैं।

आप का उपयोग करने की आवश्यकता होगी tcsetattr() ICANON मोड को बंद करने के फ़ंक्शन , और संभवतः ECHO को भी अक्षम करना होगा। मेमोरी से, आपको प्रोग्राम को बाहर निकलने पर टर्मिनल को वापस ICANON मोड में सेट करना होगा!

पूर्णता के लिए, यहां कुछ कोड मैंने अभी खटखटाए हैं (nb: no error check!) जो एक Unix TTY सेट <conio.h>करता है kbhit()और DOS फ़ंक्शन का अनुकरण करता है और getch():


1
गेटेक () माना जाता है कि किस कुंजी को वापस करना है, या क्या यह केवल चरित्र का उपभोग करना है?
Salepate

@Salepate यह चरित्र वापस करने के लिए माना जाता है। वह (void)बिट है जो उस चरित्र को प्राप्त करता है और फिर उसे फेंक देता है।
अलनीतक

1
ओह, मैं देख रहा हूं, शायद मैं इसे गलत कर रहा हूं, लेकिन जब मैं प्रिंटफ ("% c") पर getch () का उपयोग करता हूं; यह स्क्रीन पर अजीब अक्षर प्रदर्शित करता है।
सलापेट करें

थोड़ा संपादित करें, आपको #include <unistd.h> पढ़ने () के लिए काम करने की आवश्यकता है
jernkuan

क्या एक वर्ण के बजाय पूरी लाइन (उदाहरण के लिए 'गेटलाइन') को पढ़ने का एक सरल तरीका है?
आजमुक

15

select()सुविधा के लिए बहुत कम स्तर है। मेरा सुझाव है कि आप ncursesलाइब्ररी का उपयोग करने के लिए लाईब्रेरी को क्रोक मोड और डिले मोड में रखें, फिर कॉल करें getch(), ERRयदि कोई वर्ण तैयार नहीं है, तो वापस आ जाएगा :

उस बिंदु पर आप getchबिना रुके कॉल कर सकते हैं ।


मैंने शापों का उपयोग करने के बारे में भी सोचा था, लेकिन इसके साथ संभावित समस्या यह है कि initscr () कॉल स्क्रीन को साफ़ करता है, और यह शायद स्क्रीन आउटपुट के लिए सामान्य stdio का उपयोग करने के तरीके से भी मिलता है।
अलनीतक

5
हाँ, शाप सुंदर है या कुछ भी नहीं है।
नॉर्मन रैमसे

11

UNIX सिस्टम पर, आप sigactionसिग्नल के लिए SIGINTसिग्नल हैंडलर रजिस्टर करने के लिए कॉल का उपयोग कर सकते हैं जो कंट्रोल + सी कुंजी अनुक्रम का प्रतिनिधित्व करता है। सिग्नल हैंडलर एक ध्वज सेट कर सकता है जिसे लूप में चेक किया जाएगा ताकि यह उचित रूप से टूट सके।


मुझे लगता है कि मैं शायद आसानी से ऐसा करना समाप्त कर दूंगा, लेकिन मैं अन्य उत्तर को स्वीकार करने जा रहा हूं क्योंकि यह शीर्षक पूछता है।
Zxaos

6

आप शायद चाहते हैं kbhit();

यह सभी वातावरणों पर काम नहीं कर सकता है। एक पोर्टेबल तरीका एक निगरानी धागा बनाने और कुछ ध्वज को सेट करने का होगाgetch();


4
निश्चित रूप से पोर्टेबल नहीं। kbhit () एक डॉस / विंडोज कंसोल IO फ़ंक्शन है।
अलनीतक

1
यह अभी भी कई उपयोगों के लिए एक मान्य उत्तर है। ओपी ने पोर्टेबिलिटी या किसी विशेष प्लेटफॉर्म को निर्दिष्ट नहीं किया।
एएसहेल्ली

4
Alnitak: मुझे नहीं पता था कि यह एक प्लेटफ़ॉर्म निहित है - समतुल्य विंडोज शब्द क्या है?
Zxaos

3
@Alnitak प्रोग्रामिंग अवधारणा के रूप में "नॉन-ब्लॉकिंग" क्यों होगा, यूनिक्स वातावरण में अधिक परिचित होगा?
MestreLion

4
@Alnitak: मेरा मतलब है कि मैं किसी भी "निक्स" को छूने से बहुत पहले "नॉन-ब्लॉकिंग कॉल" शब्द से परिचित था। इसलिए Zxaos की तरह मुझे कभी भी यह एहसास नहीं होगा कि यह एक मंच होगा।
MestreLion

4

गैर-अवरोधक कीबोर्ड इनपुट प्राप्त करने का एक और तरीका डिवाइस फ़ाइल को खोलना और इसे पढ़ना है!

आपको उस डिवाइस फ़ाइल को जानना होगा जिसे आप खोज रहे हैं, / dev / input / event * में से एक। आप जिस डिवाइस को चाहते हैं, उसे ढूंढने के लिए आप कैट / प्रॉप / बस / इनपुट / डिवाइस चला सकते हैं।

यह कोड मेरे लिए काम करता है (एक प्रशासक के रूप में चलाएं)।


शानदार उदाहरण है, लेकिन मैं एक ऐसे मुद्दे पर भाग गया, जहां Xorg प्रमुख घटनाओं को प्राप्त करता रहेगा, जब छह से अधिक कुंजी आयोजित किए जाने पर इवेंट डिवाइस का उत्सर्जन बंद हो जाता है: \ अजीब सही?
थोरसुमोनर

3

इस उद्देश्य के लिए शाप पुस्तकालय का उपयोग किया जा सकता है। बेशक, select()और सिग्नल हैंडलर का उपयोग एक निश्चित सीमा तक भी किया जा सकता है।


1
शाप पुस्तकालय का नाम है और जब आप इसका उपयोग करेंगे तो आप क्या करेंगे?) हालाँकि, जब से मैं इसे +1 करने जा रहा हूँ।
वेन वर्नर

2

यदि आप कंट्रोल-सी को पकड़कर खुश हैं, तो यह एक सौदा है। यदि आप वास्तव में गैर अवरुद्ध I / O चाहते हैं, लेकिन आप शाप पुस्तकालय नहीं चाहते हैं, तो एक और विकल्प AT & T sfioलाइब्रेरी में लॉक, स्टॉक और बैरल को स्थानांतरित करना है । यह अच्छा पुस्तकालय है, जो C stdioपर अधिक लचीला, थ्रेड-सुरक्षित है, और बेहतर प्रदर्शन करता है। (sfio का अर्थ है सुरक्षित, तेज I / O।)


1

ऐसा करने का कोई पोर्टेबल तरीका नहीं है, लेकिन चयन () एक अच्छा तरीका हो सकता है। अधिक संभावित समाधानों के लिए http://c-faq.com/osdep/readavail.html देखें ।


1
यह उत्तर अधूरा है। tcsetattr के साथ टर्मिनल हेरफेर के बिना () [मेरा जवाब देखें] आपको fd 0 पर कुछ भी नहीं मिलेगा जब तक कि आप प्रेस नहीं करते हैं।
अलनीतक

0

आप चुन सकते हैं कि अनुसरण के रूप में चयन करें:

बहुत अधिक काम नहीं: डी


2
यह उत्तर अधूरा है। आपको टर्मिनल को गैर-विहित मोड पर सेट करने की आवश्यकता है। nfds1. सेट भी किया जाना चाहिए
लूजर

0

यहां आपके लिए यह करने के लिए एक फ़ंक्शन है। आप की जरूरत है termios.hजो POSIX सिस्टम के साथ आता है।

इसे तोड़ना: tcgetattrवर्तमान टर्मिनल जानकारी प्राप्त करता है और इसे इसमें संग्रहीत करता है t। यदि cmd1 है, तो स्थानीय इनपुट ध्वज tगैर-अवरुद्ध इनपुट पर सेट है। अन्यथा यह रीसेट है। फिर tcsetattrमानक इनपुट में परिवर्तन करता हैt

यदि आप अपने कार्यक्रम के अंत में मानक इनपुट को रीसेट नहीं करते हैं तो आपको अपने शेल में समस्याएं होंगी।


स्विच स्टेटमेंट एक ब्रैकेट को याद कर रहा है :)
étale-cohomology
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.