C engellemesiz klavye girişi


82

C (Linux'ta) kullanıcı bir tuşa basana kadar döngü yapan, ancak her döngüye devam etmek için bir tuşa basılmasını gerektirmeyen bir program yazmaya çalışıyorum.

Bunu yapmanın kolay bir yolu var mı? Sanırım onunla yapabilirim select()ama bu çok iş gibi görünüyor.

Alternatif olarak, engellemeyen io yerine program kapanmadan önce temizleme yapmak için a ctrl- ctuşa basmanın bir yolu var mı?


bir konu açmaya ne dersiniz?
Nadav B

Yanıtlar:


65

Daha önce de belirtildiği gibi, sigactionctrl-c'yi selectyakalamak veya herhangi bir standart girişi yakalamak için kullanabilirsiniz.

Bununla birlikte, ikinci yöntemle, TTY'yi bir defada satır modu yerine bir defada karakter modunda olacak şekilde ayarlamanız gerektiğini unutmayın. İkincisi varsayılandır - bir metin satırı yazarsanız, siz enter tuşuna basana kadar çalışan programın stdin'ine gönderilmez.

tcsetattr()ICANON modunu kapatmak için işlevi kullanmanız ve muhtemelen ECHO'yu da devre dışı bırakmanız gerekir. Hafızadan, program çıktığında terminali tekrar ICANON moduna ayarlamanız gerekir!

Tamlık adına, işte biraz önce devreden çıkardığım (nb: hata kontrolü yok!), Bir Unix TTY kuran ve DOS <conio.h>işlevlerini taklit eden kbhit()ve getch():


1
Getch () 'in basılan tuşa dönmesi mi gerekiyor, yoksa sadece karakteri tüketmesi mi gerekiyor?
Salepate

@Salepate, karakteri döndürmesi gerekiyor. Bu (void)karakteri alan ve sonra onu çöpe atan kısımdır.
Alnitak

1
oh anlıyorum, belki yanlış yapıyorum ama bir printf ("% c") üzerinde getch () kullandığımda; ekranda garip karakterler gösteriyor.
Salepate

Hafif düzenleme, okumak () 'ın çalışması için #include <unistd.h> gerekir
jernkuan

Tek bir karakter yerine tüm satırı okumanın (örneğin 'getline' ile) basit bir yolu var mı?
azmeuk

15

select()kolaylık sağlamak için biraz fazla düşük seviyededir. ncursesKütüphaneyi terminali cbreak moduna ve gecikme moduna koymak için kullanmanızı öneririm , ardından arama getch(), ERReğer hazır karakter yoksa geri döner :

Bu noktada getchengellemeden arayabilirsiniz .


Ben de curses kullanmayı düşündüm, ancak bununla ilgili olası sorun, initscr () çağrısının ekranı temizlemesi ve belki de ekran çıktısı için normal stdio'yu kullanma yoluna girmesidir.
Alnitak

5
Evet, lanetler ya hep ya hiçtir.
Norman Ramsey

11

UNIX sistemlerinde, Control + C tuş dizisini temsil sigactioneden sinyal için bir sinyal işleyiciyi kaydetmek için çağrıyı kullanabilirsiniz SIGINT. Sinyal işleyici, uygun şekilde kırılmasını sağlayan döngüde kontrol edilecek bir bayrak ayarlayabilir.


Sanırım bunu kolaylıkla yapacağım ama diğer cevabı da başlığın sorduğuna daha yakın olduğu için kabul edeceğim.
Zxaos

6

Muhtemelen istiyorsun kbhit();

bu tüm ortamlarda çalışmayabilir. Taşınabilir bir yol, bir izleme iş parçacığı oluşturmak ve bazı işaretler ayarlamak olabilir.getch();


4
kesinlikle taşınabilir değil. kbhit () bir DOS / Windows konsolu GÇ işlevidir.
Alnitak

1
Hala birçok kişinin kullandığı geçerli bir cevap. OP, taşınabilirliği veya belirli bir platformu belirtmedi.
AShelly

4
Alnitak: Bir platformu ima ettiğinin farkında değildim - eşdeğer Windows terimi nedir?
Zxaos

3
@Alnitak Bir programlama kavramı olarak "non-blocking" neden Unix ortamında daha tanıdık olsun?
MestreLion

4
@Alnitak: Demek istediğim, herhangi bir * nix'e dokunmadan çok önce "bloke olmayan çağrı" terimine aşinaydım .. bu yüzden Zxaos gibi bunun bir platform anlamına geleceğini asla fark etmem.
MestreLion

4

Engellemesiz klavye girişi almanın başka bir yolu da aygıt dosyasını açıp okumaktır!

Aradığınız aygıt dosyasını / dev / input / event * 'dan birini bilmelisiniz. İstediğiniz cihazı bulmak için cat / proc / bus / input / devices çalıştırabilirsiniz.

Bu kod benim için çalışıyor (yönetici olarak çalıştırın).


harika bir örnek, ancak Xorg'un, altıdan fazla anahtar tutulduğunda olay cihazı yaymayı durdurduğunda anahtar olayları almaya devam edeceği bir sorunla karşılaştım: \ tuhaf değil mi?
ThorSummoner

3

Curses kütüphanesi bu amaçla kullanılabilir. Tabii ki, select()sinyal işleyicileri de belli bir dereceye kadar kullanılabilir.


1
curses, hem kitaplığın adı hem de onu kullandığınızda mırıldanacağınız şeydir;) Ancak, size +1 olarak bahsedeceğim için.
Wayne Werner

2

Control-C'yi yakalamaktan mutluysanız, bu iş bitmiştir. Engellemesiz G / Ç'yi gerçekten istiyorsanız, ancak curses kitaplığını istemiyorsanız, başka bir alternatif kilidi, stoğu ve namluyu AT&T sfiokitaplığına taşımaktır . C üzerinde desenlenmiş güzel bir kitaplık, stdioancak daha esnek, iş parçacığı güvenli ve daha iyi performans gösteriyor. (sfio, güvenli, hızlı G / Ç anlamına gelir.)


1

Bunu yapmanın taşınabilir bir yolu yoktur, ancak select () iyi bir yol olabilir. Daha olası çözümler için http://c-faq.com/osdep/readavail.html adresine bakın .


1
bu cevap eksik. tcsetattr () ile uçbirim manipülasyonu olmadan [cevabıma bakın], enter tuşuna basana kadar fd 0'da hiçbir şey elde edemezsiniz.
Alnitak

0

Bunu aşağıdaki gibi seçerek yapabilirsiniz:

Çok fazla iş değil: D


2
Bu cevap eksik. Terminali standart olmayan moda ayarlamanız gerekir. Ayrıca nfds1 olarak ayarlanmalıdır.
luser droog

0

İşte bunu sizin için yapacak bir işlev. termios.hPOSIX sistemleriyle birlikte gelenlere ihtiyacınız var.

Bunu parçalamak: tcgetattrmevcut terminal bilgilerini alır ve içinde saklar t. Eğer cmd1, yerel giriş bayrağıdır tengellenmeyen girişine ayarlanır. Aksi takdirde sıfırlanır. Ardından tcsetattrstandart girişi olarak değiştirir t.

Programınızın sonunda standart girişi sıfırlamazsanız, kabuğunuzda sorunlar yaşarsınız.


Switch ifadesinde bir parantez eksik :)
étale-cohomology
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.